可以使用嵌入在Go(1.4.1)中实现Mixins,因为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{}
是一个例外。这段代码:
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 :为什么会这样?
答案 0 :(得分:8)
编译器是正确的。它应该如何决定,OX.F
和Y.F
应该使用哪个?它不能。因此,您可以直接调用所需的方法:使用
z.Y.F()
或
z.OX.F()
修改:至于您在[{1}}上定义F
之前您的示例有效的原因,the Spec中提到了这一点:
对于类型为
Y
或T
的值x,其中*T
不是指针或接口类型,T
表示x.f
中最浅深度的字段或方法{1}}这里有T
。 如果没有一个f
具有最浅的深度,则选择器表达式是非法的。
(强调补充。)
在定义方法之前,最浅的实现是f
。在您定义OX.F
之后,同一级别上有两个Y.F
,这是非法的。
同样,编译器是正确的。您已将F
和Y
类型嵌入OX
,而不是Z
和*Y
。正如the Spec所述,
相应指针类型
*OX
的方法集是使用receiver*T
或*T
声明的所有方法的集合(也就是说,它还包含{{1}的方法集}})。
T
包含T
的所有方法,但不是相反。 *T
和T
的方法集为空,所以显然,OX
只是将它们打印出来,好像它们是任何其他类型的结构,没有定义Y
方法。
答案 1 :(得分:2)
Ainar-G写得很整洁
对于类型为T或* T的值,其中T不是指针或接口 type,x.f表示T中最浅深度处的字段或方法 哪里有这样的f。如果没有一个f 最浅的深度,选择器表达式是非法的。
我想添加一点
如果S包含匿名字段T,则S和* S的方法集都包括带有接收方T的提升方法。* S的方法集还包括带接收方* T的提升方法。
如果你只是使用引用来推广像
这样的方法,那么事情会有效fmt.Println(&z)
但是这会导致选择的模糊性因为String方法的可能性很小,因此由于规范,选择器String是非法的。编译器必须抱怨,但事实并非如此。此行为看起来未指定,只能作为常见打印操作的特殊情况解释。 这将按预期工作
var y Y
fmt.Println(&y)
这是一个有效的例子 Playground