如果我将具有小写名称(私有)的接口嵌入到具有大写名称(public)的另一个接口中,我假设定义包之外的代码无法看到嵌入式私有接口。这个假设是否正确?
type b interface {
G() int
}
type A interface {
F() string
b
}
定义包之外的代码无法“看到”嵌入式b
,对吗?外部代码无法通过G
的实例调用A
。
答案 0 :(得分:7)
接口嵌入接口只不过是将嵌入式接口的method set合并到嵌入器中,因此它成为嵌入式类型方法集的一部分。是否导出嵌入式接口类型并不重要。引自Spec: Interface types:
接口
T
可以使用(可能合格的)接口类型名称E
来代替方法规范。这在E
中称为嵌入接口T
;它将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
的{{1}}文件,例如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()
}
的包中创建一个类型,您不需要引用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()
}
字段。此外,嵌入类型的字段和方法被提升为嵌入器,就像它们将是嵌入器的字段或方法一样。引自Spec: Struct types:
如果
A.b
是合法的selector,则结构f
中嵌入字段的字段或方法x
称为已提升该字段或方法x.f
。提升字段的作用类似于结构的普通字段,除了它们不能用作结构的composite literals中的字段名称。
定义包之外的代码不能引用嵌入字段f
,这是正确的,但定义包之外的代码可以调用提升的A.b
方法,因为此标识符不是低级的,因此限制不适用于它。
当您尝试引用它们时,编译器会强制执行对非导出标识符的限制。从另一个包写入A.G()
是编译时错误,因为您指的是未导出的标识符(a.b.G()
)。当您编写a.b
时,您不是指任何未导出的标识符,而是仅指导出的a.G()
标识符,因此允许使用。