在编译器行为中实现Mixins和不一致

Kav*_*ian 4 mixins go

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 }
Run Code Online (Sandbox Code Playgroud)

如果我们这样做:

var z Z
z.F()
Run Code Online (Sandbox Code Playgroud)

会给我们:

functionality in X.F()
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.

现在让我们OX使用方法添加另一种类型F()并将其嵌入Z:

type Z struct {
    Y
    OX
}

type OX struct{} // overriding X

func (x OX) F() {
    fmt.Println("functionality in OX.F()")
}
Run Code Online (Sandbox Code Playgroud)

有趣!现在我们得到了functionality in OX.F()一个向我们展示Go编译器搜索方法的方法,从自己键入,然后是最后一个嵌入类型.我们可以通过添加F()Z:

func (x Z) F() {
    fmt.Println("functionality in Z.F()")
}
Run Code Online (Sandbox Code Playgroud)

输出是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()")
}
Run Code Online (Sandbox Code Playgroud)

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

问题1:为什么会这样?

额外的间接水平Y意味着其他东西,但带给我的是这个.正如我猜测的那样func (t T) String() string{}是一个例外.这段代码:

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()"
}
Run Code Online (Sandbox Code Playgroud)

然后这个:

var z Z
fmt.Println(z)
Run Code Online (Sandbox Code Playgroud)

给我们:

{in Y.String() in OX.String()}
Run Code Online (Sandbox Code Playgroud)

这是合乎逻辑的.但是如果我们使用指针接收器:

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()"
}
Run Code Online (Sandbox Code Playgroud)

将打印出来:

{{{}} {}}
Run Code Online (Sandbox Code Playgroud)

问题2:为什么会这样?

Ain*_*r-G 8

问题1

编译器是正确的.它应该如何决定,使用哪个OX.F和哪个Y.F应该使用?它不能.因此,您可以直接调用所需的方法:使用

z.Y.F()
Run Code Online (Sandbox Code Playgroud)

要么

z.OX.F()
Run Code Online (Sandbox Code Playgroud)

编辑:至于为什么你的榜样的工作,直到你定义FY,这是中提到的规格:

对于类型的值x T*T其中T不是指针或接口类型,x.f表示在最浅深度域或方法T,其中有这样一个f.如果没有一个f深度最浅的选择器表达式是非法的.

(重点补充.)

在定义方法之前,最浅的实现是OX.F.在你定义之后Y.F,F在同一级别上有两个s,这是非法的.

问题2

同样,编译器是正确的.您有嵌入的类型YOXZ,不*Y*OX.正如规范中所写,

设置相应的指针型的方法*T是该组的与接收器声明的所有方法*TT(即,它也包含的方法集T).

*T有所有方法T,但不是相反.方法套OXY是空的,所以很明显,fmt.Println只是打印他们,如果他们是任何其它种类的结构没有String()定义的方法.