为何Go编译器在指针类型嵌入时,给非指针类型添加指针接收者方法?
Go嵌入指针类型时方法集的特殊行为解析
在Go语言中,结构体嵌入类型时的方法集提升规则,在嵌入值类型和指针类型时表现出不同的行为,下面通过代码示例分析并解释其逻辑。
嵌入值类型的预期行为
当结构体嵌入值类型时,方法集的表现符合常规认知:
package main import ( "fmt" "reflect" ) type MyType1 struct { MyType2 } type MyType2 struct { } func (MyType2) Function1() { } func (*MyType2) Function2() { } func main() { t1 := reflect.TypeOf(MyType1{}) t2 := reflect.TypeOf(&MyType1{}) fmt.Println(t1, "has", t1.NumMethod(), "methods:") for i := 0; i < t1.NumMethod(); i++ { fmt.Print(" method#", i, ": ", t1.Method(i).Name, "\n") } fmt.Println(t2, "has", t2.NumMethod(), "methods:") for i := 0; i < t2.NumMethod(); i++ { fmt.Print(" method#", i, ": ", t2.Method(i).Name, "\n") } }
输出结果:
main.MyType1 has 1 methods: method#0: Function1 *main.MyType1 has 2 methods: method#0: Function1 method#1: Function2
这里的规则是:
- 值类型
MyType1的方法集仅包含嵌入类型MyType2的值接收者方法Function1 - 指针类型
*MyType1的方法集包含MyType2的值接收者方法(自动提升)和*MyType2的指针接收者方法Function2
嵌入指针类型的特殊行为
当结构体改为嵌入指针类型*MyType2时,值类型MyType1的方法集也包含了*MyType2的指针接收者方法Function2:
package main import ( "fmt" "reflect" ) type MyType1 struct { *MyType2 } type MyType2 struct { } func (MyType2) Function1() { } func (*MyType2) Function2() { } func main() { t1 := reflect.TypeOf(MyType1{}) t2 := reflect.TypeOf(&MyType1{}) fmt.Println(t1, "has", t1.NumMethod(), "methods:") for i := 0; i < t1.NumMethod(); i++ { fmt.Print(" method#", i, ": ", t1.Method(i).Name, "\n") } fmt.Println(t2, "has", t2.NumMethod(), "methods:") for i := 0; i < t2.NumMethod(); i++ { fmt.Print(" method#", i, ": ", t2.Method(i).Name, "\n") } }
输出结果:
main.MyType1 has 2 methods: method#0: Function1 method#1: Function2 *main.MyType1 has 2 methods: method#0: Function1 method#1: Function2
背后的设计逻辑
这种行为并非异常,而是Go语言方法集提升规则的合理设计:
- 嵌入指针类型的方法集提升规则:当结构体
S嵌入*T时,S的方法集将包含*T的所有方法。这是因为对于S的实例s,调用s.Function2()时,Go会自动通过s中的*MyType2指针字段进行解引用,等价于执行(*s.MyType2).Function2()——这在语法上是合法的,因此该方法被提升到S的方法集中。 - 值接收者方法的继承:同时,
*T本身可以调用T的值接收者方法(Go会自动将指针转换为值),因此T的值接收者方法Function1也会被提升到S的方法集。 - 指针类型
*S的方法集:*S自然继承所有S的方法,也就是包含T和*T的全部方法,和嵌入值类型时的*S表现一致。
简单来说,Go的设计意图是让嵌入类型的方法尽可能被外部类型访问,当嵌入的是指针时,值类型结构体可以通过内部指针间接调用指针接收者方法,因此这些方法被纳入值类型的方法集。
内容的提问来源于stack exchange,提问作者user20502562
相关产品推荐
相关产品推荐

