Interfaces — Conformance checking(is,as)

In Go, interface conformance is not declared explicitly but is implicitly ensured. A type satisfies an interface if it implements all the methods declared in the interface. Go provides mechanisms to check if a type implements an interface both at compile time and runtime using type assertions and type switches.

Interface Conformance

Let's start with a basic example of defining an interface and a type that conforms to it.

package main

import "fmt"

// Define an interface
type Animal interface {
    Speak() string
}

// Define a type that implements the interface
type Dog struct {
    Name string
}

// Implement the Speak method for the Dog type
func (d Dog) Speak() string {
    return "Woof!"
}

func main() {
    // Create an instance of Dog
    var a Animal = Dog{Name: "Buddy"}

    // Call the Speak method
    fmt.Println(a.Speak()) // Output: Woof!
}

Compile-Time Conformance Checking

You can perform compile-time conformance checking by assigning a variable of your type to a variable of the interface type. If your type does not implement the interface, the compiler will produce an error.

package main

// Define an interface
type Animal interface {
    Speak() string
}

// Define a type that implements the interface
type Cat struct {
    Name string
}

// Implement the Speak method for the Cat type
func (c Cat) Speak() string {
    return "Meow!"
}

// A function that requires the Animal interface
func makeAnimalSpeak(a Animal) {
    println(a.Speak())
}

func main() {
    // This will work since Cat implements Animal
    makeAnimalSpeak(Cat{Name: "Whiskers"})

    // This will cause a compile-time error if uncommented, because int does not implement Animal
    // makeAnimalSpeak(5)
}

Runtime Conformance Checking: Type Assertion

You can use type assertions to check at runtime if a variable of an interface type holds a specific concrete type.

package main

import "fmt"

// Define an interface
type Animal interface {
    Speak() string
}

// Define a type that implements the interface
type Bird struct {
    Name string
}

// Implement the Speak method for the Bird type
func (b Bird) Speak() string {
    return "Chirp!"
}

func main() {
    var a Animal = Bird{Name: "Tweety"}

    // Type assertion to check if a is of type Bird
    bird, ok := a.(Bird)
    if ok {
        fmt.Printf("a is of type Bird and says: %s\\\\n", bird.Speak()) // Output: a is of type Bird and says: Chirp!
    } else {
        fmt.Println("a is not of type Bird")
    }
}

Runtime Conformance Checking: Type Switch

A type switch is a convenient way to handle multiple type assertions.

package main

import "fmt"

// Define an interface
type Animal interface {
    Speak() string
}

// Define types that implement the interface
type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return "Woof!"
}

type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return "Meow!"
}

type Bird struct {
    Name string
}

func (b Bird) Speak() string {
    return "Chirp!"
}

func main() {
    var a Animal = Dog{Name: "Buddy"}

    // Type switch to determine the actual type of a
    switch v := a.(type) {
    case Dog:
        fmt.Printf("a is a Dog named %s and says: %s\\\\n", v.Name, v.Speak())
    case Cat:
        fmt.Printf("a is a Cat named %s and says: %s\\\\n", v.Name, v.Speak())
    case Bird:
        fmt.Printf("a is a Bird named %s and says: %s\\\\n", v.Name, v.Speak())
    default:
        fmt.Println("a is of an unknown type")
    }
}

Summary

By using these mechanisms, you can ensure that your types conform to the expected interfaces both at compile time and runtime, thus leveraging Go's powerful type system and interface mechanics.

interfaces — Methods Requirements