Golang中指针类型嵌入时为什么编译器会将指针接收者方法添加到非指针类型的方法集中
Golang中指针类型嵌入时为什么编译器会将指针接收者方法添加到非指针类型的方法集中 标题可能有点令人困惑,因为我不确定如何在不举例和详细说明的情况下用一句话(一个问题)来解释清楚。
为了清楚地解释标题中的问题含义,让我从一个符合我预期的情况开始说起。
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")
}
}
当类型嵌入是基于“类型”(不是准确的名称,但我们姑且这么称呼)时,一切都符合我的预期。结构体 MyType1 有一个 MyType2 嵌入字段(“类型”嵌入字段),因此类型 MyType1 的方法集中将包含方法 (MyType1)Function1(),而类型 *MyType1 的方法集中将包含方法 (*MyType1)Function1() 和 (*MyType1)Function2()。所以,每个类型(MyType1 和 *MyType1)都会获得它们对应的方法。*MyType1 会获得方法 (*MyType1)Function1(),因为它是从 (MyType1)Function1() 隐式产生的。因此,正如我之前所说,一切都在预料之中。为了证明这一点,我还使用了标准的“reflect”包,并得到了以下打印输出:
main.MyType1 has 1 methods:
method#0: Function1
*main.MyType1 has 2 methods:
method#0: Function1
method#1: Function2
当我将“类型”嵌入字段替换为“指针类型”嵌入字段(MyType1 现在拥有 *MyType2 而不是 MyType2)时,奇怪的行为发生了。所以代码看起来像这样:
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
所以,不知何故,MyType1 也拥有了方法 (MyType1)Function2(),即使它并未被声明为类型 MyType2 的值接收者方法。
有人能对此给出合乎逻辑的解释吗?为什么会发生这种情况?
更多关于Golang中指针类型嵌入时为什么编译器会将指针接收者方法添加到非指针类型的方法集中的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这是Go语言方法集和嵌入语义的设计特性。当嵌入指针类型时,编译器会将指针接收者方法提升到包含类型的值接收者方法集中,这是为了保持方法调用的可用性。
根据Go语言规范,当类型S嵌入类型T(或*T)时:
- 如果嵌入的是
T,则S的方法集包含T的值接收者方法 - 如果嵌入的是
*T,则S的方法集包含T的所有方法(包括指针接收者方法)
这是因为当嵌入指针类型时,即使包含类型是值类型,嵌入的字段仍然是一个有效的指针,可以安全地调用指针接收者方法。
示例代码演示:
package main
import "fmt"
type MyType2 struct {
value int
}
func (m MyType2) ValueMethod() {
fmt.Println("Value method called")
}
func (m *MyType2) PointerMethod() {
if m == nil {
fmt.Println("Called on nil pointer")
} else {
fmt.Println("Pointer method called, value:", m.value)
}
}
// 嵌入值类型
type EmbeddedValue struct {
MyType2
}
// 嵌入指针类型
type EmbeddedPointer struct {
*MyType2
}
func main() {
// 情况1:嵌入值类型
ev := EmbeddedValue{MyType2{value: 10}}
ev.ValueMethod() // 正常调用
// ev.PointerMethod() // 编译错误:无法通过值类型调用指针方法
evp := &EmbeddedValue{MyType2{value: 20}}
evp.ValueMethod() // 正常调用
evp.PointerMethod() // 正常调用
// 情况2:嵌入指针类型
ep := EmbeddedPointer{&MyType2{value: 30}}
ep.ValueMethod() // 正常调用
ep.PointerMethod() // 正常调用 - 关键区别!
epp := &EmbeddedPointer{&MyType2{value: 40}}
epp.ValueMethod() // 正常调用
epp.PointerMethod() // 正常调用
// 即使嵌入nil指针也能调用方法
epNil := EmbeddedPointer{nil}
epNil.ValueMethod() // 正常调用(接收者为nil)
epNil.PointerMethod() // 正常调用(接收者为nil)
}
关键机制在于:
- 当
MyType1嵌入*MyType2时,MyType1的方法集包含MyType2的所有方法 - 这是因为嵌入的字段已经是
*MyType2类型,可以直接作为指针接收者使用 - 编译器自动生成包装方法,将调用转发给嵌入字段
这种设计确保了:
- 嵌入指针类型时,方法集的完整性
- 即使包含类型是值类型,也能调用嵌入类型的指针方法
- 保持Go语言简洁的嵌入语义
reflect包的结果验证了这一行为:当嵌入*MyType2时,MyType1获得了Function2方法,即使它是指针接收者方法。


