type b interface {
G() int
}
type A interface {
F() string
b
}
定义包之外的代码无法 "看到" 嵌入的 b
,对吗?外部代码无法通过 A
的实例调用 G
。
type b interface {
G() int
}
type A interface {
F() string
b
}
定义包之外的代码无法 "看到" 嵌入的 b
,对吗?外部代码无法通过 A
的实例调用 G
。
在接口中嵌入接口,实际上就是将嵌入接口的方法集合并到嵌入者中,使其成为嵌入者类型的方法集的一部分。无论嵌入的接口类型是否被导出都没有关系。引用规范:接口类型:
接口
T
可以使用(可能带限定符的)接口类型名E
代替方法规范。这被称为在T
中嵌入接口E
;它会将E
的所有(导出和非导出)方法添加到接口T
中。
在你的代码中,所有发生的事情就是 A
将成为一个具有 2 个方法的接口:F() string
和 G() int
。不会有像 A.b
这样的“字段”,因为 A
是一个接口类型,而不是结构体。因此,这并不特别“有趣”。
如何尝试它?
在任何包中创建一个名为 subplay
的文件夹,subplay.go
:
package subplay
type b interface {
G() int
}
type A interface {
F() string
b
}
创建另一个go
文件,导入这个subplay
,例如:play.go
:
package main
import "play/subplay"
func main() {
var a subplay.A
a.G()
}
type aimpl struct{}
func (aimpl) F() string { return "aimpl.F() called" }
func (aimpl) G() int { return 1 }
func NewA() A {
return aimpl{}
}
在play.go
中:
func main() {
var a subplay.A
a = subplay.NewA()
a.G()
}
上述代码可以编译和运行,不会出现错误。
还请注意,您可以在另一个包中创建实现subplay.A
的类型,您不需要引用subplay.b
,因为方法集是唯一重要的内容。下面的another
类型也实现了subplay.A
,您可以将其放置在play.go
中:
type another struct{}
func (another) F() string { return "aimpl.F() called" }
func (another) G() int { return 1 }
func main() {
var a subplay.A
a = another{}
a.G()
}
这段代码可以编译和运行,不会出现运行时错误。
更有趣的情况是,在结构体(而不是接口)中嵌入未公开的结构体(或接口)类型,这会“真正”创建一个A.b
字段。此外,嵌入类型的字段和方法会像嵌入到它们自己的结构体中一样被提升到嵌入者中。引用Spec: Struct types::
如果
x.f
是合法的selector,则称结构体x
中嵌入字段f
的字段或方法f
为提升的。提升的字段就像结构体的普通字段一样,只是不能在结构体的composite literals中用作字段名。
定义包之外的代码无法引用嵌入字段A.b
,这是正确的,但定义包之外的代码可以调用提升的A.G()
方法,因为此标识符不是小写的,所以该限制不适用于它。
a.b.G()
是一个编译时错误,因为您正在引用非导出标识符(a.b
)。当您写a.G()
时,您不是在引用任何非导出标识符,只是在引用导出的a.G
标识符,因此是允许的。G
会因为在 b
的“后面”而被隐藏起来。谢谢。 - RalphA.b
字段一起出现,那么a.b.G()
将会被轻松调用。您在这里的意思是什么?不太清楚。 - Shahriara
是 nil
。如果您将非 nil
值分配给它(实现了 subplay.A
),那么 a.G()
调用就不会引发恐慌。请参见编辑后的答案。 - iczax.f
是一个合法的选择器..."。如果你的结构体嵌入了两种不同类型,它们都有一个相同名称的字段或方法,那么 x.f
将不是合法的,而且 x.f
选择器将在编译时出现错误。 - icza