Mixins 可以在 Go (1.4.1) 中使用嵌入来实现,并且由于 struct{} 不占用内存(据我所知)它适合我们想要添加一些功能或只是向一个实际上可能没有任何内容的类型添加一个方法的情况处理它的状态,但我们喜欢避免 ParseThing(...) 而是写 thing.Parse(...)

所以有:

type X struct{}

func (x X) F() {
    fmt.Println("functionality in X.F()")
}

type Y struct{ X }
type Z struct{ Y }

那么如果我们这样做:
var z Z
z.F()

会给我们:
functionality in X.F()

到现在为止还挺好。

现在让我们使用 OX 方法添加另一种类型的 F() 并将其嵌入到 Z 中:
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) F() {
    fmt.Println("functionality in OX.F()")
}

有趣的!现在我们得到 functionality in OX.F() ,它向我们展示了 Go 编译器搜索该方法,从它自己的类型开始,然后是最后一个嵌入的类型。我们可以通过将 F() 添加到 Z 来检查:
func (x Z) F() {
    fmt.Println("functionality in Z.F()")
}

输出是 functionality in Z.F() 。现在,如果我们删除 Z.F() 方法并将 F() 添加到 Y :
//func (x Z) F() {
//    fmt.Println("functionality in Z.F()")
//}

func (x Y) F() {
    fmt.Println("functionality in Y.F()")
}

然后我们看到这个错误 ambiguous selector z.F ;通过指针重定向没有区别。

问题 1: 为什么会这样?

额外的间接层 Y 意味着其他东西,但让我想到了这一点。正如我猜测的那样 func (t T) String() string{} 是一个异常(exception)。这段代码:
type X struct{}

func (x X) String() string {
    return "in X.String()"
}

type Y struct{ X }
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) String() string {
    return "in OX.String()"
}

func (x Y) String() string {
    return "in Y.String()"
}

然后这个:
var z Z
fmt.Println(z)

给我们:
{in Y.String() in OX.String()}

这是合乎逻辑的。但是如果我们使用指针接收器:
import (
    "fmt"
    "testing"
)

func TestIt(t *testing.T) {
    var z Z
    fmt.Println(z)
}

type X struct{}

func (x *X) String() string {
    return "in X.String()"
}

type Y struct{ X }
type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x *OX) String() string {
    return "in OX.String()"
}

func (x *Y) String() string {
    return "in Y.String()"
}

会打印出来:
{{{}} {}}

问题 2 :为什么会这样?

最佳答案

问题 1
编译器是正确的。它应该如何决定应该使用 OX.FY.F 中的哪一个?它不能。因此,直接调用所需的方法取决于您:要么使用

z.Y.F()
或者
z.OX.F()
编辑: 至于为什么您的示例在您在 F 上定义 Y 之前一直有效,这在 the Spec 中提到:

(加了重点。)
在定义方法之前,最浅的实现是 OX.F 。定义 Y.F 后,同一层出现了两个 F ,这是非法的。
问题2
同样,编译器是正确的。您已将 YOX 类型嵌入到 Z 中,而不是 *Y*OX 。正如 the Spec 所写,
*T 具有 T 的所有方法,但反之则不然。 OXY 的方法集是空的,所以很明显,fmt.Println 只是将它们打印出来,就好像它们是没有定义 String() 方法的任何其他类型的结构一样。

关于go - 实现 Mixin 和编译器行为的不一致,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28281564/

10-14 16:30