是否可以只导出嵌入式结构实现的方法子集? 这是一种非常不同的方法来减少代码的复制和粘贴,还有一种更惯用的方法吗?
type A struct {
}
func (a *A) Hello() {
fmt.Println("Hello!")
}
func (a *A) World() {
fmt.Println("World!")
}
type B struct {
A
}
type C struct {
A
}
func main() {
b := B{}
c := C{}
// B should only export the Hello - function
b.Hello()
// C should export both Hello - and World - function
c.Hello()
c.World()
}
答案 0 :(得分:5)
这就是嵌入工作的方式,你无能为力。 (实际上有,最后会看到肮脏的伎俩。)
虽然可以通过接口实现您想要的功能。使您的结构未导出,(B
=> b
和C
=> c
),并创建"构造函数"类似函数,返回接口类型,只包含您要发布的方法:
type b struct {
A
}
type c struct {
A
}
type Helloer interface {
Hello()
}
type HelloWorlder interface {
Helloer
World()
}
func NewB() Helloer {
return &b{}
}
func NewC() HelloWorlder {
return &c{}
}
您可能希望将接口和功能调用为不同,这仅用于演示。
另请注意,虽然返回的Helloer
界面不包含World()
方法,但仍有可能达到"它使用type assertion,例如:
h := NewB() // h is of type Helloer
if hw, ok := h.(HelloWorlder); ok {
hw.World() // This will succeed with the above implementations
}
在Go Playground上尝试此操作。
如果类型嵌入A
类型,A
获取提升的方法将成为嵌入式类型method set的一部分(因此成为A
类型的方法)。这在Spec: Struct types:
如果
f
是合法{{3>},则x
中的匿名字段的字段或methodx.f
称为已提升表示该字段或方法f
。
重点是促销,选择器必须为合法。 selector描述了x.f
如何解决:
以下规则适用于选择器:
- 对于类型
醇>x
或T
的值*T
,T
不是指针或接口类型,x.f
表示字段或方法T
中最浅的深度,其中有f
。如果没有具有最浅深度的Spec: Selectors,则选择器表达式是非法的。[...]
这是什么意思?只需嵌入,B.World
将表示B.A.World
方法,因为它位于最浅的深度。但是,如果我们实现以使B.A.World
不是最浅的,则类型B
不会使用此World()
方法,因为B.A.World
未获得晋升。
我们如何实现这一目标?我们可能会添加名为World
的字段:
type B struct {
A
World int
}
此B
类型(或更确切地说*B
)没有World()
方法,因为B.World
表示字段而不是{{ 1}}因为前者处于最浅的深度。请在one f
上尝试此操作。
同样,这并不妨碍任何人明确引用B.A.World
,因此可以达到""我们所取得的成就是B.A.World()
或B
类型没有*B
方法。
这个"脏伎俩的另一个变种"是利用第一条规则的结尾:"如果不是一个 World()
深度最浅" 。这可以实现也嵌入另一种类型,另一种结构也具有f
字段或方法,例如:
World
在Go Playground上尝试此变体。