接口定义与隐式实现
本教程共 80 篇 · 第 41 篇 · 更新于 2026-07-27 · 约 7 分钟阅读
41. 接口定义与隐式实现
本节目标:搞懂 Go 接口是什么,学会定义接口,理解隐式实现和鸭子类型的设计理念。
接口是什么
接口(Interface)是一组方法签名的集合。它定义了「能做什么」,但不关心「怎么做」。
打个比方:你有一个 USB 接口,只要设备能插进去就能用,不管是鼠标、键盘还是 U 盘。接口只规定形状(方法签名),不规定内部实现。
type Speaker interface {
Speak() string
}
这个接口说:「只要你有 Speak() 方法并且返回 string,你就是 Speaker」。
隐式实现
Go 的接口实现是隐式的。你不需要写 implements 关键字,只要一个类型实现了接口要求的所有方法,它就自动满足这个接口。
package main
import "fmt"
// 定义接口
type Speaker interface {
Speak() string
}
// 定义类型
type Dog struct {
Name string
}
// Dog 实现了 Speak 方法
func (d Dog) Speak() string {
return d.Name + ": 汪汪汪"
}
type Cat struct {
Name string
}
// Cat 也实现了 Speak 方法
func (c Cat) Speak() string {
return c.Name + ": 喵喵喵"
}
func main() {
animals := []Speaker{
Dog{Name: "Rex"},
Cat{Name: "Whiskers"},
}
for _, a := range animals {
fmt.Println(a.Speak())
}
// Rex: 汪汪汪
// Whiskers: 喵喵喵
}
Dog 和 Cat 都没有声明自己实现了 Speaker,但它们都有 Speak() string 方法,所以都能放进 []Speaker 里。
Note这叫「鸭子类型」:如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。Go 在编译时检查类型是否实现了接口的所有方法,比运行时鸭子类型更安全。
接口的好处:松耦合
接口最大的价值是解耦。调用方只依赖接口,不依赖具体类型,换实现时不影响调用方:
package main
import "fmt"
// 接口定义行为
type Notifier interface {
Notify(message string) error
}
// 邮件通知
type EmailNotifier struct{}
func (e EmailNotifier) Notify(message string) error {
fmt.Println("发送邮件:", message)
return nil
}
// 短信通知
type SMSNotifier struct{}
func (s SMSNotifier) Notify(message string) error {
fmt.Println("发送短信:", message)
return nil
}
// 调用方只依赖接口
func sendMessage(n Notifier, msg string) {
n.Notify(msg)
}
func main() {
sendMessage(EmailNotifier{}, "你好") // 发送邮件: 你好
sendMessage(SMSNotifier{}, "你好") // 发送短信: 你好
}
sendMessage 函数不知道也不关心传进来的是邮件还是短信,它只要求参数实现了 Notifier 接口。以后要加微信通知,写个新类型就行,sendMessage 一行都不用改。
多方法接口
接口可以有多个方法:
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
一个类型必须实现所有方法才算满足接口。少一个都不行。
TipGo 社区有个建议:接口越小越好。只定义你需要的方法,不要塞一堆方法进去。小接口更容易被满足,组合性更强。标准库的
io.Reader只有一个Read方法,但几乎所有涉及读取的类型都实现了它。
任何类型都能实现接口
不只是结构体,任何自定义类型都能实现接口:
type Score int
func (s Score) String() string {
return fmt.Sprintf("%d分", int(s))
}
// Score 实现了 fmt.Stringer 接口
func main() {
s := Score(95)
fmt.Println(s) // 95分 Println 会自动调用 String()
}
甚至函数类型也行:
type Handler func()
func (h Handler) Run() {
h()
}
type Runner interface {
Run()
}
func main() {
var r Runner = Handler(func() {
fmt.Println("running")
})
r.Run() // running
}
接口组合
接口可以组合其他接口,形成更大的接口:
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
}
满足 ReadWriter 的类型必须同时满足 Reader 和 Writer。这个后面接口组合章节会详细讲。
小结
接口是 Go 类型系统的灵魂:
- 接口定义方法签名集合,不提供实现
- 实现是隐式的,不用
implements关键字 - 只要类型实现了接口的所有方法,就自动满足接口
- 接口实现松耦合,调用方只依赖行为不依赖具体类型
- 接口越小越好,用组合构建大接口
下一章讲接口值的内部结构和类型断言。