接口

隐式实现

Go 的接口是一组方法签名集合。类型无需显式声明实现了某接口,只要方法集满足要求即视为实现,这被称为"鸭子类型"的静态化:

package main

import "fmt"

// 接口定义:只声明行为
type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
type Robot struct{ ID int }

// Dog 实现了 Speaker
func (d Dog) Speak() string { return d.Name + ": 汪汪" }

// Robot 实现了 Speaker
func (r Robot) Speak() string { return fmt.Sprintf("Robot-%d: 您好", r.ID) }

// 面向接口编程:函数只依赖接口,不依赖具体类型
func announce(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    announce(Dog{Name: "Rex"})
    announce(Robot{ID: 1})
}
Tip

隐式实现意味着可以为无法修改源码的类型(包括标准库类型)定义接口。建议接口定义在消费方一侧而非实现方一侧,保持接口最小化。

接口组合

小接口可以组合成大接口,这是标准库的常见模式:

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
}

类型断言

从接口值中提取具体类型:

package main

import "fmt"

func describe(v any) {
    // 1. 单值断言:类型不符时 panic,应仅在确定类型时使用
    // s := v.(string)

    // 2. comma-ok 断言:安全的惯用法
    if s, ok := v.(string); ok {
        fmt.Println("字符串:", s)
        return
    }
    if n, ok := v.(int); ok {
        fmt.Println("整数:", n)
        return
    }
    fmt.Println("未知类型")
}

func main() {
    describe("hello")
    describe(42)
    describe(3.14)
}

类型 switch

对多种类型分支判断时,类型 switch 更简洁:

package main

import "fmt"

func classify(v any) string {
    switch x := v.(type) {
    case nil:
        return "空值"
    case int, int64:
        return fmt.Sprintf("整数: %v", x)
    case string:
        return "字符串: " + x
    case []string:
        return fmt.Sprintf("字符串切片(长度 %d)", len(x))
    default:
        return "其他类型"
    }
}

func main() {
    fmt.Println(classify(nil))
    fmt.Println(classify(100))
    fmt.Println(classify("Go"))
    fmt.Println(classify([]string{"a", "b"}))
}

空接口与 any

空接口 interface{} 不含任何方法,因此所有类型都实现它。Go 1.18 起提供别名 any,语义相同:

func printAll(vals ...any) {
    for _, v := range vals {
        fmt.Println(v)
    }
}
Tip

any 只应用于真正"任意类型"的场景(如 JSON 解析、通用容器)。若类型集合有限,优先使用泛型或具体接口,以保留编译期检查。

常见标准库接口

接口方法含义
errorError() string错误
io.ReaderRead(p []byte) (int, error)读取数据源
io.WriterWrite(p []byte) (int, error)写入目标
fmt.StringerString() string自定义打印格式
sort.InterfaceLen/Less/Swap可排序
context.ContextDeadline/Done/Err/Value取消信号与元数据

指针接收者与接口

方法集规则需要注意:只有指针类型拥有指针接收者的方法。若方法使用指针接收者,则仅 *T 满足接口,T 不满足:

type Counter interface{ Inc() }

type counter struct{ n int }

func (c *counter) Inc() { c.n++ } // 指针接收者

func main() {
    var c counter
    var i Counter = &c // 必须取地址:&c 才实现 Counter
    _ = i
}

小结

  • 隐式实现使接口定义与使用解耦,接口应保持小而聚焦。
  • 类型断言与类型 switch 用于从接口还原具体类型。
  • 指针接收者方法仅属于指针类型,赋值给接口时需取地址。