Go中嵌入式私有接口的可见性

时间:2018-02-21 13:05:41

标签: go interface visibility embedding

如果我将具有小写名称(私有)的接口嵌入到具有大写名称(public)的另一个接口中,我假设定义包之外的代码无法看到嵌入式私有接口。这个假设是否正确?

type b interface {
    G() int
}

type A interface {
    F() string
    b
}

定义包之外的代码无法“看到”嵌入式b,对吗?外部代码无法通过G的实例调用A

1 个答案:

答案 0 :(得分:7)

在接口

中嵌入未导出的接口

接口嵌入接口只不过是将嵌入式接口的method set合并到嵌入器中,因此它成为嵌入式类型方法集的一部分。是否导出嵌入式接口类型并不重要。引自Spec: Interface types:

  

接口T可以使用(可能合格的)接口类型名称E来代替方法规范。这在E中称为嵌入接口T;它将E的所有(导出和非导出)方法添加到接口T

您的代码中发生的一切都是A将是一个包含2种方法的界面:F() stringG() 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

这会在没有运行时恐慌的情况下再次编译和运行。

在struct

中嵌入未导出的接口

类似且更有趣的情况是在结构中(而不是在接口中)嵌入未导出的结构(或接口)类型,“真正”创建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()标识符,因此允许使用。