# Go 语言接口（Interface）

接口是 Go 语言中最核心的抽象机制之一。与其他语言不同，Go 的接口是**隐式实现**的——类型不需要显式声明"我实现了某个接口"，只要它实现了接口要求的全部方法，就自动满足该接口。这种设计让 Go 的代码更加灵活、松耦合。本篇将全面介绍接口的定义、使用、类型断言以及最佳实践。

## 接口的定义

使用 `type ... interface` 语法定义接口，接口是一组**方法签名**的集合：

```go
package main

import "fmt"

// 定义一个 Shape 接口
type Shape interface {
    Area() float64
    Perimeter() float64
}

func main() {
    // 接口是抽象的，不能直接实例化
    var s Shape
    fmt.Println(s) // <nil>
}
```

上面的 `Shape` 接口要求实现 `Area()` 和 `Perimeter()` 两个方法。任何类型只要同时实现了这两个方法，就自动成为 `Shape` 接口的实现。

## 隐式实现（Duck Typing）

Go 不需要 `implements` 关键字。只要类型实现了接口中定义的所有方法，就自动满足接口：

```go
package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

// Circle 实现了 Area 方法，自动满足 Shape 接口
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rect struct {
    Width, Height float64
}

// Rect 也实现了 Area 方法，同样满足 Shape 接口
func (r Rect) Area() float64 {
    return r.Width * r.Height
}

// 接收接口类型的函数
func PrintArea(s Shape) {
    fmt.Printf("面积: %.2f\n", s.Area())
}

func main() {
    c := Circle{Radius: 5}
    r := Rect{Width: 4, Height: 6}

    PrintArea(c) // 面积: 78.54
    PrintArea(r) // 面积: 24.00
}
```

> **Duck Typing**：如果一个东西走起来像鸭子、叫起来像鸭子，那它就是鸭子。Go 接口同理——不关心你是什么类型，只关心你能做什么。

![接口实现关系](https://img.zhaojq.top/20260729161720394.png "接口实现关系")

## 接口是一组方法签名

接口只声明方法的签名（参数和返回值），不包含实现。不同的类型可以用各自的方式实现同一组方法：

```go
package main

import "fmt"

// Speaker 接口只有一个方法
type Speaker interface {
    Speak() string
}

type Dog struct{}
type Cat struct{}

func (d Dog) Speak() string   { return "汪汪!" }
func (c Cat) Speak() string   { return "喵~" }

func main() {
    animals := []Speaker{Dog{}, Cat{}}
    for _, a := range animals {
        fmt.Println(a.Speak())
    }
    // 输出:
    // 汪汪!
    // 喵~
}
```

## 空接口 interface{}

空接口不包含任何方法，因此**所有类型**都实现了空接口。在 Go 1.18+ 中，可以使用 `any` 作为 `interface{}` 的别名：

```go
package main

import "fmt"

func main() {
    // interface{} 可以存储任意类型的值
    var anything interface{}

    anything = 42
    fmt.Println(anything) // 42

    anything = "hello"
    fmt.Println(anything) // hello

    anything = []int{1, 2, 3}
    fmt.Println(anything) // [1 2 3]
}
```

空接口常用于编写接受任意类型参数的通用函数（在泛型出现之前）。例如 `fmt.Println` 的参数类型就是 `interface{}`。

## 类型断言

接口变量存储的是 `(value, type)` 对。类型断言用于从接口值中提取具体的底层类型：

```go
package main

import "fmt"

func main() {
    var i interface{} = "hello"

    // 单返回值：类型不匹配时会 panic
    s := i.(string)
    fmt.Println(s) // hello

    // 安全的类型断言：value, ok 模式
    s, ok := i.(string)
    if ok {
        fmt.Println("是字符串:", s)
    }

    // 类型不匹配时，ok 为 false，不会 panic
    n, ok := i.(int)
    fmt.Println(n, ok) // 0 false
}
```

> **注意**：单返回值的类型断言如果类型不匹配会引发 panic，生产代码中务必使用 `value, ok` 模式。

## type switch

当需要对接口值进行多种类型的判断时，`type switch` 比连续的类型断言更简洁：

```go
package main

import "fmt"

func describe(i interface{}) string {
    switch v := i.(type) {
    case int:
        return fmt.Sprintf("整数: %d", v)
    case string:
        return fmt.Sprintf("字符串: %q", v)
    case bool:
        return fmt.Sprintf("布尔值: %t", v)
    case float64:
        return fmt.Sprintf("浮点数: %.2f", v)
    default:
        return fmt.Sprintf("未知类型: %T", v)
    }
}

func main() {
    fmt.Println(describe(42))       // 整数: 42
    fmt.Println(describe("hello"))  // 字符串: "hello"
    fmt.Println(describe(true))     // 布尔值: true
    fmt.Println(describe(3.14))     // 浮点数: 3.14
    fmt.Println(describe([]int{}))  // 未知类型: []int
}
```

## 接口组合

Go 支持通过**嵌入**其他接口来组合新接口：

```go
package main

import "fmt"

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// ReadWriter 组合了 Reader 和 Writer
type ReadWriter interface {
    Reader
    Writer
}

// 只要同时实现了 Read 和 Write，就自动满足 ReadWriter
type Buffer struct {
    data []byte
}

func (b *Buffer) Read(p []byte) (n int, err error)  { return 0, nil }
func (b *Buffer) Write(p []byte) (n int, err error)  { return len(p), nil }

func main() {
    var rw ReadWriter = &Buffer{}
    fmt.Printf("%T 实现了 ReadWriter\n", rw)
}
```

## 常用标准库接口

Go 标准库中定义了大量接口，以下是几个最常用的：

### error 接口

```go
// error 是 Go 中最常用的接口
type error interface {
    Error() string
}
```

### fmt.Stringer 接口

```go
package main

import "fmt"

type User struct {
    Name string
    Age  int
}

// 实现 fmt.Stringer 接口
func (u User) String() string {
    return fmt.Sprintf("%s(%d岁)", u.Name, u.Age)
}

func main() {
    u := User{Name: "张三", Age: 25}
    fmt.Println(u) // 张三(25岁) — fmt.Println 会自动调用 String()
}
```

### io.Reader 和 io.Writer

```go
package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    // strings.NewReader 返回的类型实现了 io.Reader
    r := strings.NewReader("Hello, Go!")

    buf := make([]byte, 5)
    n, err := r.Read(buf)
    if err != nil && err != io.EOF {
        panic(err)
    }
    fmt.Printf("读取 %d 字节: %s\n", n, buf[:n]) // 读取 5 字节: Hello

    // 继续读取
    n, err = r.Read(buf)
    fmt.Printf("读取 %d 字节: %s\n", n, buf[:n]) // 读取 5 字节: , Go!
}
```

## 接口最佳实践

### 小接口原则

Go 推崇**小接口**——接口只包含 1-2 个方法，职责单一。标准库中的 `io.Reader`、`io.Writer`、`fmt.Stringer` 都是单方法接口。

```go
// 好的设计：小而专注的接口
type Writer interface {
    Write(p []byte) (n int, err error)
}

// 不好的设计：接口过于庞大
type DoEverything interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
    Close() error
    Seek(offset int64, whence int) (int64, error)
    // ... 更多方法
}
```

### 接受接口，返回结构体

函数参数尽量接收接口（提高灵活性），返回值尽量返回具体类型（让调用者知道确切类型）：

```go
package main

import "fmt"

type Greeter interface {
    Greet() string
}

type EnglishGreeter struct{}

func (g EnglishGreeter) Greet() string { return "Hello!" }

// 参数接收接口，提高灵活性
func PrintGreeting(g Greeter) {
    fmt.Println(g.Greet())
}

func main() {
    PrintGreeting(EnglishGreeter{})
}
```

### 在需要时定义接口，而非提前

不要一开始就定义大量接口。当有多个类型需要以相同方式处理时，再提取接口：

```go
package main

import "fmt"

// 先有具体类型
type Circle struct{ Radius float64 }
type Square struct{ Side float64 }

// 当发现多处需要 Area() 时，再提取接口
type Shaper interface {
    Area() float64
}

func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius }
func (s Square) Area() float64 { return s.Side * s.Side }

func TotalArea(shapes []Shaper) float64 {
    total := 0.0
    for _, s := range shapes {
        total += s.Area()
    }
    return total
}

func main() {
    shapes := []Shaper{
        Circle{Radius: 5},
        Square{Side: 4},
    }
    fmt.Printf("总面积: %.2f\n", TotalArea(shapes))
}
```

## 总结

本篇介绍了 Go 接口的核心机制：接口通过方法签名定义行为规范，类型通过隐式实现满足接口，无需显式声明；空接口可以存储任意类型；类型断言和 type switch 用于从接口值中提取具体类型；接口组合让我们可以灵活构建更大的抽象。遵循小接口原则，在需要时定义接口，是写出优雅 Go 代码的关键。

