在接口中嵌入未导出的接口
接口嵌入接口无非是将嵌入接口的method set合并到嵌入器中,因此成为嵌入器类型的方法集的一部分。是否导出嵌入式接口类型都没有关系。引用自Spec: Interface types:
接口T 可以使用(可能限定的)接口类型名称E 代替方法规范。这在T中称为嵌入接口E;它将E 的所有(导出和非导出)方法添加到接口T。
在您的代码中发生的所有事情是 A 将是一个具有 2 个方法的接口:F() string 和 G() int。不会有像A.b 这样的“字段”,因为A 是接口类型,而不是结构。所以这并不是特别“有趣”。
你怎么试?
在任意包中创建文件夹subplaysubplay.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()
}
然后编译。发生运行时恐慌是因为a 未初始化(或者更确切地说是nil),但如果是这样,a.G() 调用就不会恐慌。
添加以下内容将不会出现运行时恐慌:
在subplay.go,添加:
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()
}
以上代码编译运行,不会panic。
另外请注意,您可以在另一个实现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 是表示该字段或方法f 的合法selector,则结构x 中嵌入字段的字段或方法f 被称为提升。
提升字段的作用类似于结构的普通字段,但它们不能用作结构的composite literals 中的字段名称。
定义包之外的代码不能引用嵌入字段A.b,这是正确的,但是定义包之外的代码可以调用提升的A.G()方法,因为这个标识符不是小写,所以限制不适用于它。
当您尝试引用它们时,编译器会强制执行对非导出标识符的限制。从另一个包写入a.b.G() 是编译时错误,因为您指的是未导出的标识符(a.b)。当你写a.G()时,你不是指任何非导出的标识符,只是指导出的a.G标识符,所以是允许的。