Go语言多态仅通过接口实现,不依赖继承;只要类型方法集完全匹配接口方法签名(含接收者类型、大小写、参数及返回值),即自动满足接口,编译期检查,运行时动态分发。

Go 语言的多态不靠继承,只靠接口;只要方法签名完全匹配,类型就自动满足接口——这是你写对代码的前提,也是你调试时绕不开的底层逻辑。
为什么 func (t T) Method() 和 func (t *T) Method() 不能混用
接口实现不是看“有没有这个方法”,而是看“方法集是否包含该方法”。值类型 T 的方法集只包含以 T 为接收者的方法;指针类型 *T 的方法集则同时包含 T 和 *T 接收者的方法。
- 如果你定义了
func (t *T) Read() []byte,那只有*T能赋值给io.Reader,T{}直接报错:cannot use T{} (type T) as type io.Reader in argument - 反过来,如果只实现了
func (t T) Write(),那你不能把&t当作io.Writer传给需要修改内部状态的函数(比如日志写入器),因为方法没改到原值 - 标准库如
json.Unmarshal明确要求传入指针,因为它要往里写数据;而fmt.Println接收fmt.Stringer时,T和*T都行——取决于你实现的是哪个版本
interface{} 不是万能胶水,它是类型擦除的起点
空接口能存任何值,但取出来时必须显式还原类型。它不提供行为抽象,只提供“暂存”能力。滥用会导致运行时 panic 或隐藏逻辑错误。
- 直接对
interface{}调用方法会编译失败:invalid operation: cannot call non-function value i (type interface {}) - 用
i.(string)强断言失败会 panic;安全做法是用双值断言:s, ok := i.(string) - JSON 反序列化常用
map[string]interface{},但嵌套深了之后类型推导极难维护;建议尽早转成结构体,别长期停留在空接口层
组合小接口比定义大接口更可靠
Go 标准库几乎全是单方法接口(io.Reader、io.Writer、fmt.Stringer),这不是偷懒,是为降低实现门槛和提升复用率。
立即学习“go语言免费学习笔记(深入)”;
- 定义一个含 5 个方法的大接口,意味着所有实现者都得写满 5 个桩函数,哪怕其中 3 个永远返回
nil - 实际中更常见的是组合:比如
type ReadWriter interface { io.Reader; io.Writer },这样os.File自动满足,无需额外声明 - 测试时也方便 mock:只需实现
Read就能测读逻辑,不用搭一整套“可读可写可关闭可寻址”的模拟对象
接口真正的复杂点不在语法,而在设计边界——你得想清楚:这个行为是“谁都能做”,还是“只有某类实体才该做”;方法签名里要不要暴露错误、要不要支持取消、要不要返回指针。这些决定一旦固化,后续就很难在不破坏兼容性的情况下调整。


















