--- tags: [go, golang, go基础语法, 接口] create time: 2026-06-07 15:00 --- # Go 语言接口 ## 概述 Go 的接口是**隐式实现**——不需要 `implements` 关键字,只要类型拥有接口声明的所有方法,就自动实现了该接口。本文覆盖接口定义、空接口、类型断言、接口嵌套等核心概念。 ## 正文 ### 为什么 Go 接口是"鸭嘴兽"式的? > "如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。" Go 接口是**行为契约**:不要求类型显式声明"我实现了这个接口",只要你的类型有对应的方法签名,就默认实现。这种设计让新类型可以天然适配已有接口。 ```go type Reader interface { Read(p []byte) (n int, err error) } // 任何有 Read([]byte) (int, error) 签名的类型都是 Reader // 无需 implements 或 extends ``` > [!question] ❓ 思考 > 如果一个结构体有 `Read` 方法但没声明过要实现 `Reader` 接口,它能被接收 `Reader` 参数的函数调用吗?(答案:能!) ### 接口定义与实现 ```go type Phone interface { Call() SendMessage() } type Apple struct{ Name string } func (a Apple) Call() { fmt.Println(a.Name, "打电话") } func (a Apple) SendMessage() { fmt.Println(a.Name, "发短信") } type Huawei struct{ Name string } func (h Huawei) Call() { fmt.Println(h.Name, "打电话") } func (h Huawei) SendMessage() { fmt.Println(h.Name, "发短信") } // Apple 和 Huawei 都自动实现了 Phone 接口 var p Phone = Apple{"iPhone"} p.Call() // iPhone 打电话 p = Huawei{"Mate"} p.SendMessage() // Mate 发短信 ``` #### 多接口实现 一个类型可以实现多个接口: ```go type Reader interface { Read() } type Writer interface { Write() } type ReadWriter struct{} func (rw ReadWriter) Read() {} func (rw ReadWriter) Write() {} // ReadWriter 同时实现了 Reader 和 Writer ``` ### 空接口 `interface{}` 没有方法的接口就是空接口——**所有类型都实现了它**。 ```go var any interface{} any = 42 // int any = "hello" // string any = true // bool // fmt.Println 的参数就是 ...interface{} fmt.Println(any, 3.14, "world") ``` > [!info] ℹ️ Go 1.20+ 的 `any` 别名 > `any` 是 `interface{}` 的内置别名,语义等价但更简洁: > ```go > func Process(v any) { ... } > ``` ### 类型断言 从空接口中取出原始值需要**类型断言**: ```go x := getValue() // 返回 interface{} // 方式一:带 ok 判断(推荐) val, ok := x.(int) if ok { fmt.Println("是 int:", val) } else { fmt.Println("不是 int") } // 方式二:直接取值(失败会 panic) val := x.(int) // ❌ 如果 x 不是 int,程序崩溃 ``` > [!warning] ⚠️ 忘记 ok 的危险 > `x.(int)` 在类型不匹配时会 **panic**。生产代码中始终使用 `val, ok := x.(T)` 形式。 #### switch 类型断言 当不确定具体类型时,用 `switch` + `.(type)` 处理多种可能: ```go func describe(i interface{}) { switch v := i.(type) { case int: fmt.Printf("int: %d\n", v) case string: fmt.Printf("string: %s\n", v) case bool: fmt.Printf("bool: %t\n", v) default: fmt.Printf("unknown: %T\n", i) // %T 打印动态类型 } } describe(42) // int: 42 describe("hi") // string: hi describe(nil) // unknown: ``` > [!note] 📝 nil interface vs interface(nil) > - `var x interface{} = nil` → `x == nil` 为 true > - `var x interface{} = (*int)(nil)` → `x != nil`(类型信息存在,只是值为 nil) ### 接口作函数参数 这是 Go 中最常见的接口用法——让函数接受"行为"而非"具体类型": ```go type Speaker interface { Speak() string } func Greet(s Speaker) { fmt.Println("Hello,", s.Speak()) } type Dog struct{ Name string } func (d Dog) Speak() string { return d.Name } type Human struct{ Name string } func (h Human) Speak() string { return h.Name } Greet(Dog{"Rex"}) // Hello, Rex Greet(Human{"Alice"}) // Hello, Alice ``` > [!tip] 💡 依赖倒置 > 函数应该依赖接口而非具体类型。这样新增类型时无需修改现有函数代码——符合开闭原则。 ### 接口嵌套 接口可以嵌入其他接口,组合出更大的契约: ```go type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } // Closer 组合了 Reader 和 Writer 的所有方法 type Closer interface { Reader Writer Close() error } // 实现 Closer 需要实现 Read/Write/Close 三个方法 ``` > [!info] ℹ️ io 包的经典嵌套 > `io.ReadCloser`、`io.WriteCloser`、`io.ReadWriteCloser` 都是 stdlib 中嵌套接口的实际应用。 ### 常见陷阱总结 | 陷阱 | 说明 | 修复 | |------|------|------| | nil 接口 vs 含 nil 值的接口 | `(*T)(nil)` 赋值给接口后不为 nil | 注意区分 | | 类型断言不加 ok | 类型不匹配会 panic | 始终用 `v, ok := x.(T)` | | 接口不能作为 map key | 接口类型不可比较(除非底层类型可比较) | 用其他字段做 key | | 空接口失去类型安全 | 取回时需要断言 | Go 1.20+ 用泛型替代部分场景 |