← Go EnglishChapter 09 of 13

Interfaces

## Learning Objectives - Understand interfaces as contracts - Implement interfaces implicitly - Use empty interface - Master type assertions and switches - Design with interfaces ## What is an Interface? An interface defines a contract. Types implementing an interface must provide all specified methods. ## Declaration ### Basic Interface ```go type Speaker interface { Speak() string } ``` ### Multiple Methods ```go type Animal interface { Speak() string Move() string } ``` ## Implementation ### Implicit Implementation Types implement interfaces implicitly - no explicit declaration. ```go type Dog struct { Name string } func (d Dog) Speak() string { return "Woof!" } func main() { var s Speaker = Dog{Name: "Buddy"} fmt.Println(s.Speak()) // Woof! } ``` ### Both Implementations ```go type Cat struct{} func (c Cat) Speak() string { return "Meow!" } type Cow struct{} func (c Cow) Speak() string { return "Moo!" } func main() { animals := []Speaker{Cat{}, Cow{}} for _, animal := range animals { fmt.Println(animal.Speak()) } } ``` ## Empty Interface ### interface{} (empty interface) ```go var i interface{} = "hello" i = 42 i = true ``` ### Accept Anything ```go func printAll(items ...interface{}) { for _, item := range items { fmt.Println(item) } } printAll(1, "hello", true, 3.14) ``` ## Type Assertions ### Basic Assertion ```go var i interface{} = "hello" s := i.(string) // Panics if not string fmt.Println(s) // hello s, ok := i.(string) // Safe: ok is false if not string fmt.Println(s, ok) // hello true ``` ### Assert Wrong Type ```go var i interface{} = 42 s, ok := i.(string) fmt.Println(s, ok) // "" false (no panic) ``` ### Panic on Failure ```go var i interface{} = 42 s := i.(string) // PANIC: interface conversion failed ``` ## Type Switch ### switch on Type ```go func describe(i interface{}) { switch v := i.(type) { case int: fmt.Printf("Integer: %d\n", v) case string: fmt.Printf("String: %s (length %d)\n", v, len(v)) case bool: fmt.Printf("Boolean: %t\n", v) case float64: fmt.Printf("Float64: %.2f\n", v) default: fmt.Printf("Unknown type: %T\n", v) } } func main() { describe(42) describe("hello") describe(true) } ``` ### Type Switch with Interface ```go type Shape interface { Area() float64 } type Circle struct{ Radius float64 } type Rectangle struct{ Width, Height float64 } func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius } func (r Rectangle) Area() float64 { return r.Width * r.Height } func printArea(s Shape) { switch shape := s.(type) { case Circle: fmt.Printf("Circle with radius %.2f, area %.2f\n", shape.Radius, shape.Area()) case Rectangle: fmt.Printf("Rectangle %.2f x %.2f, area %.2f\n", shape.Width, shape.Height, shape.Area()) default: fmt.Println("Unknown shape") } } ``` ## Interface Values ### Structure An interface value has two components: 1. Dynamic type 2. Dynamic value ```go var s Speaker fmt.Printf("%+v\n", s) // s = Dog{Name: "Buddy"} fmt.Printf("%+v\n", s) // {Name:Buddy} ``` ### nil Interface ```go var s Speaker = nil if s == nil { fmt.Println("Interface is nil") } ``` ### Interface with nil Value ```go var s Speaker = (*Dog)(nil) if s == nil { fmt.Println("Interface is nil") } else { fmt.Println("Interface is not nil, but value is nil") } ``` ## Errors ### error Interface ```go type error interface { Error() string } ``` ### Custom Error ```go type DivisionError struct { Dividend, Divisor float64 } func (e *DivisionError) Error() string { return fmt.Sprintf("cannot divide %.2f by zero", e.Dividend) } func divide(a, b float64) (float64, error) { if b == 0 { return 0, &DivisionError{Dividend: a, Divisor: b} } return a / b, nil } ``` ### Using Errors ```go result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) return } fmt.Println("Result:", result) ``` ## io.Reader and io.Writer ### io.Reader ```go type Reader interface { Read(p []byte) (n int, err error) } ``` ### io.Writer ```go type Writer interface { Write(p []byte) (n int, err error) } ``` ### Using Readers ```go import "io" import "strings" reader := strings.NewReader("Hello, World!") buf := make([]byte, 5) for { n, err := reader.Read(buf) if err == io.EOF { break } fmt.Printf("Read %d bytes: %s\n", n, buf[:n]) } ``` ### Using Writers ```go import "os" file, err := os.Create("output.txt") if err != nil { log.Fatal(err) } defer file.Close() n, err := file.Write([]byte("Hello, World!")) fmt.Printf("Wrote %d bytes\n", n) ``` ## Stringer ### fmt.Stringer ```go type Stringer interface { String() string } ``` ### Stringer Implementation ```go type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf("%s (%d)", p.Name, p.Age) } func main() { p := Person{Name: "Alice", Age: 30} fmt.Println(p) // Alice (30) } ``` ## Interface Composition ### Embedding Interfaces ```go type Reader interface { Read(p []byte) (n int, err error) } type Closer interface { Close() error } type ReadCloser interface { Reader Closer } ``` ## Best Practices ### Accept Interfaces, Return Structs ```go func NewService(repo Repository) Service { return &service{repo: repo} } type Service interface { DoSomething() error } ``` ### Keep Interfaces Small ```go // Small interface (preferred) type Writer interface { Write([]byte) (int, error) } // Large interface (avoid) type Everything interface { Read([]byte) (int, error) Write([]byte) (int, error) Close() error // ... } ``` ## nil Interface Check ```go func doSomething(i interface{}) { if i == nil { fmt.Println("nil interface") return } fmt.Printf("Value: %v, Type: %T\n", i, i) } ``` ## Summary - Interfaces define contracts (sets of methods) - Types implement interfaces implicitly - Empty interface `interface{}` accepts any type - Type assertions: `i.(T)` with comma-ok idiom - Type switch: `switch v := i.(type)` - error interface: `Error() string` - io.Reader and io.Writer are common interfaces - Accept interfaces, return concrete types - Keep interfaces small and focused

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →