我从 Java 转到 Go,有些事情让我感到困惑。
例如,让我们考虑以下代码:
package main
import (
"fmt"
)
type I interface {
Do()
MegaDo()
}
type A struct {
}
func (a *A) Do() {
fmt.Println("A")
}
func (a *A) MegaDo() {
a.Do()
}
type B struct {
A
}
func (a *B) Do() {
fmt.Println("B")
}
var i I
func main() {
fmt.Println("Hello, playground")
var i I = &B{}
i.MegaDo()
}
这里我们有一个接口(interface) I
和方法 Do()
和 MegaDo()
。结构 A
在内部实现了方法和 MegaDo
调用 Do
。 B
由 A
组成,仅覆盖 Do()
如果我在 Java 中模仿相同的代码,我希望它打印“B”。但在 Go 中它打印“A”。
虽然我有点理解它为什么会发生(因为它是嵌入而不是继承),但我想知道如何在 Go 中模仿同样的事情。 例如,我有两个仅略有不同的相同接口(interface)的实现。在这种情况下如何最大限度地重用代码?我无法相信,为了在一个实现中自定义一些逻辑,我必须复制粘贴所有内容,并只修复我的代码中的一小部分。 也许在 Go 中有一些惯用的方法可以做到这一点?
最佳答案
Go 没有“类”的子类化或扩展。嵌入类型的方法使用它们的原始类型接收器。在这种情况下,方法 MegaDo
在 B
中被提升,但在调用时,它是在 A
字段上调用的。 B.MegaDo()
只是 B.A.MegaDo()
的语法糖。因此,当它在其接收器上调用 Do()
时,它调用的是 A
版本,而不是 B
版本。
处理这个问题的更简单方法是嵌入一个接口(interface)。例如:
https://play.golang.org/p/ZPdK8zsy5_w
type Mega struct {
I
}
func (m Mega) MegaDo() {
m.Do()
}
func main() {
var a A
var b B
m := Mega{I: A}
m.MegaDo()
m.I = B
m.MegaDo()
}
注意:在这种情况下实际上不需要嵌入接口(interface),因为 MegaDo()
可以简单地调用 m.i.Do()
如果它是一个命名字段。但是,嵌入它允许其他代码直接调用 m
上的 Do()
,而不知道该字段中实际嵌入的是什么类型。另请注意,嵌入接口(interface)的实际结果是根据定义嵌入接口(interface)的结构也实现了相同的接口(interface)。
此模式的实际示例:嵌入 sql.DB 和 sql.Tx 类型(QueryRow、Query、Exec 等)的联合方法的数据库句柄类型。该句柄的用户可以调用这些方法,而不必知道它们是否在事务上下文中被调用。
关于oop - 在 Go 中模仿适当的动态调度的惯用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49114057/