Golang中如何从内嵌结构体调用外层结构体的重载方法
Golang中如何从内嵌结构体调用外层结构体的重载方法 考虑以下示例:
package main
import (
"fmt"
)
type A struct {}
func (a *A) TheMethod() {
fmt.Println("TheMethod (A's implementation)")
}
type B struct {
A
}
func (b *B) TheMethod() {
fmt.Println("TheMethod (B's implementation)")
}
func (a *A) TheMethodCallingTheMethod() {
a.TheMethod()
}
func main() {
foo := B{}
foo.TheMethodCallingTheMethod()
}
输出结果是 TheMethod (A's implementation),这在一定程度上是可以理解的。
但是,理想情况下,它不应该是 TheMethod (B's implementation) 吗?
理由是 B 已经可以透明地看到 A 的方法。那么,为什么当从 B 的实例调用 A 的 TheMethodCallingTheMethod 时,它不能透明地识别出是 B 在调用它,而不是 A,从而调用 B 的 TheMethod 而不是 A 的呢?
更多关于Golang中如何从内嵌结构体调用外层结构体的重载方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在Go语言中,结构体类型和接口类型之间存在一个重要区别。你尝试的操作在接口上可以生效,因为对接口方法的调用会动态分派给接口内部的实际类型。
但结构体始终只是结构体。你不能将A类型转换为B类型,也不能将B类型转换为A类型。TheMethodCallingTheMethod方法的接收器类型是A,它只会被A类型的结构体调用,而不会是其他类型。
foo.TheMethodCallingTheMethod()这行代码只是foo.A.TheMethodCallingTheMethod()调用的语法糖,这将在编译时确定(而不是在运行时动态确定)。因此,该方法接收到的不是结构体B,而只是嵌入的结构体A。
下面的代码可能更清楚地展示了正在发生的情况:
unwrappedA := foo.A
unwrappedA.TheMethodCallingTheMethod()
所以,unwrappedA没有对其父级foo的引用,它“不知道”自己是B结构体的一部分,它也可能是任何其他结构体或切片的一部分。它只是一个内存地址,并使用在编译时确定的静态调用。
在Go语言中,当内嵌结构体调用外层结构体的重载方法时,行为是由Go的方法调用机制决定的。内嵌结构体的方法调用遵循静态分派规则,而不是动态分派。
在你的示例中,TheMethodCallingTheMethod 是定义在类型 A 上的方法。当通过 B 的实例调用这个方法时,Go会首先查找 B 是否有这个方法。由于 B 没有直接定义 TheMethodCallingTheMethod,但通过内嵌 A 获得了这个方法,所以调用的是 A 的 TheMethodCallingTheMethod。
在 A.TheMethodCallingTheMethod 内部,a.TheMethod() 中的接收者 a 是 *A 类型,因此调用的是 A 的 TheMethod 实现,而不是 B 的重载版本。
这是Go语言设计上的选择:方法调用基于接收者的静态类型。要实现你期望的行为,需要在 B 中重写 TheMethodCallingTheMethod 方法。
示例代码:
package main
import (
"fmt"
)
type A struct{}
func (a *A) TheMethod() {
fmt.Println("TheMethod (A's implementation)")
}
type B struct {
A
}
func (b *B) TheMethod() {
fmt.Println("TheMethod (B's implementation)")
}
func (a *A) TheMethodCallingTheMethod() {
a.TheMethod()
}
// 在B中重写TheMethodCallingTheMethod
func (b *B) TheMethodCallingTheMethod() {
b.TheMethod()
}
func main() {
foo := &B{}
foo.TheMethodCallingTheMethod() // 现在输出: TheMethod (B's implementation)
}
或者,如果你希望保持单一的方法定义,可以使用接口来实现动态分派:
package main
import (
"fmt"
)
type MethodCaller interface {
TheMethod()
}
type A struct{}
func (a *A) TheMethod() {
fmt.Println("TheMethod (A's implementation)")
}
type B struct {
A
}
func (b *B) TheMethod() {
fmt.Println("TheMethod (B's implementation)")
}
func TheMethodCallingTheMethod(caller MethodCaller) {
caller.TheMethod()
}
func main() {
foo := &B{}
TheMethodCallingTheMethod(foo) // 输出: TheMethod (B's implementation)
}
在第二个示例中,通过接口 MethodCaller 实现了动态分派,这样 TheMethodCallingTheMethod 函数会根据传入的实际类型调用相应的方法实现。


