Go接口多态关键在于方法签名严格匹配:方法名、参数与返回值类型、接收者类型(值/指针)、大小写必须完全一致,否则编译失败;接口仅含方法签名,不可有字段或func关键字,实现是隐式的。

Go 里定义接口并实现多态,关键不是“怎么写语法”,而是“方法接收器类型必须和接口方法签名严格匹配”——不匹配就编译失败,根本走不到运行时多态那步。
接口定义必须只含方法签名,不能有字段或 func 关键字
接口是纯契约,只声明行为,不带任何实现或数据。常见错误包括在接口里加变量、用 func 关键字、或大小写写错导致方法不可导出:
- ❌
type Logger interface { prefix string; Log(msg string) }(接口不能有字段) - ❌
type Reader interface { func Read() []byte }(func多余) - ❌
type speaker interface { Speak() string }(首字母小写,其他包无法使用) - ✅ 正确:
type Speaker interface { Speak() string }
结构体实现接口时,值接收器和指针接收器决定能否赋值给接口
这是最容易翻车的地方:接口方法签名里写的是 *T 还是 T,直接决定你能不能把 T{} 或 &T{} 赋给该接口变量。
- 如果接口方法接收器是
*T(如func (t *User) Save() error),那么只有*User类型能赋值给该接口;User{}会报错:cannot use User{} (value of type User) as type Saver in assignment: User does not implement Saver (Save method has pointer receiver) - 如果所有接口方法接收器都是
T(如func (u User) Name() string),则User{}和&User{}都可以赋值(Go 自动解引用) - 混用接收器类型会导致部分方法“不算数”:一个结构体既有
(u User) Get()又有(u *User) Set(),它只满足“全值接收器”或“全指针接收器”的接口,不满足混合要求的接口
多态生效的前提是统一用接口变量调用,而非具体类型
真正体现多态的不是“多个结构体都实现了同一个接口”,而是你在业务逻辑中只依赖接口类型,不感知具体实现:
立即学习“go语言免费学习笔记(深入)”;
type Notifier interface {
Send(msg string) error
}
<p>type EmailNotifier struct{}
func (e EmailNotifier) Send(msg string) error { /<em> ... </em>/ }</p><p>type SlackNotifier struct{}
func (s <em>SlackNotifier) Send(msg string) error { /</em> ... */ }</p><p>func NotifyAll(n Notifier, msg string) {
n.Send(msg) // 这里才是多态调用点
}</p><p>// 调用时:
NotifyAll(EmailNotifier{}, "hello") // OK,值类型满足
NotifyAll(&SlackNotifier{}, "hello") // OK,指针类型满足
// NotifyAll(SlackNotifier{}, "hello") // ❌ 编译失败:SlackNotifier 值类型没实现 Send(因接收器是 *SlackNotifier)
注意:Go 的多态是编译期静态检查 + 运行时动态分发,没有虚函数表那一套,但底层通过 iface 结构体查表调用,有轻微性能开销(比如无法内联、可能逃逸)。
想强制检查是否实现接口,用 var _ InterfaceName = (*ConcreteType)(nil)
虽然 Go 是隐式实现,但大型项目里常需要提前发现“我以为实现了,其实漏了一个方法”的问题。在包初始化位置加一行编译期断言最可靠:
-
var _ Speaker = (*Dog)(nil):检查*Dog是否满足Speaker -
var _ Speaker = Dog{}:检查Dog值类型是否满足 - 这行代码不执行,只参与类型检查;如果结构体没实现全部方法,编译直接失败,错误信息明确指出缺哪个方法
别依赖文档或人脑记忆方法集——接口和结构体分散在不同文件时,这个技巧能省下大量调试时间。


















