Lesson 10: Interfaces¶
Big Picture¶
classDiagram
class Shape {
<<interface>>
+Area() float64
+Perimeter() float64
}
class Rectangle {
+float64 Width
+float64 Height
}
class Circle {
+float64 Radius
}
Shape <|.. Rectangle : implements (implicitly!)
Shape <|.. Circle : implements (implicitly!)
note for Shape "No implements keyword, method set is enough"
Interfaces are satisfied implicitly — if a type has the methods, it is the interface. This decouples definition from usage and is the foundation of Go's io.Reader, error, and fmt.Stringer idioms.
Concepts¶
Defining Interfaces¶
Implementing (Implicit)¶
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// Rectangle now implements Shape
Type Assertion¶
Type Switch¶
Empty Interface¶
Because every Go value satisfies the empty interface, a slice of interface{} can hold values of different types:
The modern spelling is []any; it means exactly the same thing as []interface{}. Each element retains its dynamic type, which you can inspect with a type switch:
for _, item := range data {
switch value := item.(type) {
case string:
fmt.Println("string:", value)
case int:
fmt.Println("int:", value)
case float64:
fmt.Println("float64:", value)
case bool:
fmt.Println("bool:", value)
}
}
Use heterogeneous slices only when values genuinely have different types. Prefer a concrete slice such as []string or []Employee when every element has the same shape, because concrete types provide stronger compile-time checks.
Code Walkthrough¶
package main
import "fmt"
type Speaker interface{ Speak() string }
type Dog struct{}
func (Dog) Speak() string { return "woof" }
func say(s Speaker) { fmt.Println(s.Speak()) }
func main() {
say(Dog{})
var v any = 42 // empty interface
if n, ok := v.(int); ok { // type assertion
fmt.Println("int:", n)
}
}
Full program: code/10_interfaces.go.
Exercises
- Add a
Triangletype that satisfiesShape— notice you didn't modifyShape. - Write a
type switchoveranythat handlesint,string, and[]byte. - Implement
fmt.Stringer(String() string) on a struct andPrintlnit.
Check Your Understanding¶
How does a Go type declare that it implements an interface?
What is the modern alias for interface{} (Go 1.18+)?