仅导出由嵌入式结构实现的方法子集
Export only subset of methods implemented by embedded struct
是否可以仅导出嵌入式结构实现的一部分方法?
这是一种非常不同寻常的减少代码复制和粘贴的方法吗?还有更惯用的方法吗?
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()
}
这就是嵌入的工作原理,您对此无能为力。 (其实有的,看文末的黑招)
你想要的也许可以通过接口来实现。使您的结构未导出,(B
=> b
和 C
=> c
),并创建类似函数的“构造函数”,return 接口类型,仅包含您希望发布的方法:
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{}
}
您可能希望调用不同的接口和函数,这只是为了演示。
另请注意,虽然 returned 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
的(字段和)方法得到 提升 将成为 [=58= 的一部分]
A field or method f
of an anonymous field in a struct x
is called promoted if x.f
is a legal selector that denotes that field or method f
.
重点是促销,选择者必须合法。 Spec: Selectors 描述了 x.f
是如何解决的:
The following rules apply to selectors:
- For a value
x
of type T
or *T
where T
is not a pointer or interface type, x.f
denotes the field or method at the shallowest depth in T
where there is such an f
. If there is not exactly one f
with shallowest depth, the selector expression is illegal.
[...]
这是什么意思?简单地通过嵌入,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
表示 字段 而不是 B.A.World
因为前者处于最浅的深度。在 Go Playground.
上试试这个
同样,这不会阻止任何人显式引用 B.A.World()
,因此可以“到达”并调用该方法,我们所实现的只是类型 B
或 *B
没有 World()
方法。
这个“肮脏的把戏”的另一个变体是利用第一条规则的结尾:“如果 不完全是一个 f
深度最浅。这也可以嵌入另一种类型,另一种结构,它也有一个 World
字段或方法,例如:
type hideWorld struct{ World int }
type B struct {
A
hideWorld
}
在 Go Playground 上试试这个变体。
是否可以仅导出嵌入式结构实现的一部分方法? 这是一种非常不同寻常的减少代码复制和粘贴的方法吗?还有更惯用的方法吗?
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()
}
这就是嵌入的工作原理,您对此无能为力。 (其实有的,看文末的黑招)
你想要的也许可以通过接口来实现。使您的结构未导出,(B
=> b
和 C
=> c
),并创建类似函数的“构造函数”,return 接口类型,仅包含您希望发布的方法:
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{}
}
您可能希望调用不同的接口和函数,这只是为了演示。
另请注意,虽然 returned 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
的(字段和)方法得到 提升 将成为 [=58= 的一部分]
A field or method
f
of an anonymous field in a structx
is called promoted ifx.f
is a legal selector that denotes that field or methodf
.
重点是促销,选择者必须合法。 Spec: Selectors 描述了 x.f
是如何解决的:
The following rules apply to selectors:
- For a value
x
of typeT
or*T
whereT
is not a pointer or interface type,x.f
denotes the field or method at the shallowest depth inT
where there is such anf
. If there is not exactlyone f
with shallowest depth, the selector expression is illegal.[...]
这是什么意思?简单地通过嵌入,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
表示 字段 而不是 B.A.World
因为前者处于最浅的深度。在 Go Playground.
同样,这不会阻止任何人显式引用 B.A.World()
,因此可以“到达”并调用该方法,我们所实现的只是类型 B
或 *B
没有 World()
方法。
这个“肮脏的把戏”的另一个变体是利用第一条规则的结尾:“如果 不完全是一个 f
深度最浅。这也可以嵌入另一种类型,另一种结构,它也有一个 World
字段或方法,例如:
type hideWorld struct{ World int }
type B struct {
A
hideWorld
}
在 Go Playground 上试试这个变体。