讲解
接口定义「一组方法的集合」:type Shape interface { Area() float64 } 表示「任何有 Area 方法的类型都是 Shape」。Go 接口最革命性的设计是「隐式实现」——不需要写 implements 之类的声明,一个类型只要方法齐全就自动满足接口。Circle 有 Area 方法,那它就是 Shape,编译器来验证。这把接口的定义方和使用方解耦:标准库的 io.Reader 只有一个 Read 方法,文件、网络连接、压缩流全都天然满足它。
Go 社区崇尚小接口:一个方法的接口遍地都是(Reader、Writer、Stringer),两三个方法的算常见,大接口被视为坏味道。fmt.Stringer 是最实用的例子:给类型定义 String() string 方法,fmt.Println 打印该类型时就会自动调用它,调试输出立刻变得人性化。
空接口 any(interface{} 的别名,Go 1.18 起)表示「任何类型」,是泛型出现前装任意值的容器。把 any 还原成具体类型用「类型断言」:v.(string),配合 comma ok 形式 s, ok := v.(string) 避免断言失败 panic;需要按类型分派时用 type switch,一次处理多种可能。
示例
接口的隐式实现与多态调用:
package main
import (
"fmt"
"math"
)
type Shape interface {
Area() float64
}
type Circle struct{ R float64 }
type Rect struct{ W, H float64 }
func (c Circle) Area() float64 { return math.Pi * c.R * c.R }
func (r Rect) Area() float64 { return r.W * r.H }
func printArea(s Shape) { // 参数是接口,任何 Shape 都能传
fmt.Printf("%T 的面积: %.2f\n", s, s.Area())
}
func main() {
printArea(Circle{R: 1})
printArea(Rect{W: 3, H: 4})
}
实现 fmt.Stringer 定制打印输出:
package main
import "fmt"
type Color int
const (
Red Color = iota
Green
Blue
)
func (c Color) String() string {
switch c {
case Red:
return "红"
case Green:
return "绿"
case Blue:
return "蓝"
}
return "未知"
}
func main() {
fmt.Println(Red, Green, Blue) // fmt 自动调用 String()
}
any、类型断言与 type switch:
package main
import "fmt"
func describe(v any) string {
switch x := v.(type) {
case int:
return fmt.Sprintf("整数 %d", x)
case string:
return fmt.Sprintf("字符串,长度 %d", len(x))
default:
return "其他类型"
}
}
func main() {
fmt.Println(describe(42))
fmt.Println(describe("你好"))
fmt.Println(describe(3.14))
s, ok := any("hello").(string) // comma ok 形式的类型断言
fmt.Println(s, ok)
}
常见坑
- 值方法与指针方法的方法集混淆:指针接收者的方法只属于 *T 的方法集——Circle 用指针接收者实现 Area 后,Circle 值不满足 Shape,只有 *Circle 满足。传值还是传指针要和方法集对齐。
- 设计大而全的接口:一上来定义十几个方法的接口,实现方苦不堪言,mock 测试更苦不堪言。接口应由使用方按需要定义,小接口自然好实现、好替换。
- 裸类型断言不带 ok:v.(string) 在 v 不是 string 时直接 panic。生产代码永远用两返回值形式,或 type switch。
- 把 any 当泛型用:any 装进值后类型信息就退化成运行期断言,滥用等于放弃了静态类型检查。Go 1.18 后有真正的泛型(后面章节),能用泛型就别用 any。
小结
接口是方法集合,隐式实现、宜小不宜大;Stringer 定制打印;any 配类型断言或 type switch。下一节学习 Go 的招牌设计:error 错误处理。