go中方法必须绑定到命名类型(如type id int),不能绑定到类型别名(如type myint = int);接收者用值或指针取决于是否需修改状态及性能考量;t与*t方法集不同,影响接口实现;嵌入字段的方法提升是语法糖,非继承。

方法必须绑定到命名类型,不能是基础类型别名
Go 里 func (t T) Name() 这种写法,T 必须是**你定义的命名类型**,比如 type User struct{} 或 type ID int。直接写 type MyInt = int(类型别名)不行——编译器会报 cannot define methods on non-local type int。
常见错误:把 type Status = string 当成可绑定方法的类型,结果一加方法就报错。
-
type Status string✅ 可以绑定方法 -
type Status = string❌ 编译失败 - 即使
Status和string底层一样,Go 也只认“命名类型”这个壳子
接收者用指针还是值?看是否要修改状态或避免拷贝
接收者类型不是风格选择,而是语义和性能问题。值接收者每次调用都复制整个实例;指针接收者共享原值,还能改它。
典型场景:
- 结构体较大(比如含 slice、map、大数组)→ 优先用
*T避免拷贝 - 方法要修改字段(如
u.SetName("x"))→ 必须用*T - 小结构体(如
type Point struct{ x, y int })→ 值接收者更轻量,且更安全(不会意外改原值) - 混用会导致接口实现不一致:如果某个接口要求
*T方法,而你只实现了T版本,那*T实例也不能满足该接口
方法集差异导致接口实现失败
这是最常踩的坑:T 和 *T 的方法集不一样。接口能否被满足,取决于变量的类型,而不是底层值。
比如:
type Speaker interface { Speak() }
func (t T) Speak() {}
func (t *T) Greet() {}
那么:
-
var t T; var s Speaker = t✅ 满足(T有Speak) -
var pt *T; var s Speaker = pt✅ 也满足(*T可隐式解引用调T的值接收者方法) - 但反过来:如果只有
func (t *T) Speak(),那么var t T; s = t❌ 编译失败——T没有Speak方法
所以定义接口时,要确认调用方传的是值还是指针,再决定接收者类型。
嵌入字段的方法提升不是“继承”,而是语法糖
嵌入 type A struct{ B } 后,A 看起来能直接调 B.Foo(),但这只是编译器自动补路径的糖,Foo 的接收者仍是 B,不是 A。
关键影响:
- 如果
B.Foo()是值接收者,那A.Foo()调用时,实际是先取a.B的副本再调,改不到原始B字段 - 如果
B.Foo()是指针接收者,那A.Foo()调用时,a.B必须可寻址(比如&a),否则报cannot call pointer method on a.B - 嵌入不会让
A实现B的接口——除非显式声明func (a A) Foo() { a.B.Foo() }
别把它当 OOP 继承用,它只是字段 + 方法路径的自动展开。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











