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

Visibility of embedded private interfaces in Go

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

type b interface {
    G() int
}

type A interface {
    F() string
    b
}

定义包之外的代码不能"see"嵌入b,对吗?外部代码无法通过 A.

的实例调用 G

在界面中嵌入未导出的界面

接口嵌入接口无非就是合并method set of the embedded interface into the embedder, so it becomes part of the method set of the embedder type. It doesn't matter if the embedded interface type is exported or not. Quoting from Spec: Interface types:

An interface T may use a (possibly qualified) interface type name E in place of a method specification. This is called embedding interface E in T; it adds all (exported and non-exported) methods of E to the interface T.

您的代码中发生的所有事情是 A 将是一个具有 2 个方法的接口:F() stringG() int。不会有像 A.b 这样的“字段”,因为 A 是接口类型,而不是结构。所以这不是特别“有趣”。

你怎么能试试呢?

在任意包中创建一个文件夹subplaysubplay.go:

package subplay

type b interface {
    G() int
}

type A interface {
    F() string
    b
}

创建另一个导入此 subplay 的文件 go,例如play.go:

package main

import "play/subplay"

func main() {
    var a subplay.A
    a.G()
}

并编译。虽然因为 a 未初始化(或者更确切地说是 nil)而发生运行时恐慌,但如果它被初始化,a.G() 调用将不会恐慌。

添加以下内容将不会出现 run-time 恐慌:

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()
}

这再次编译和运行,没有 run-time 恐慌。

在结构中嵌入未导出的接口

一个类似且更有趣的案例是将未导出的结构(或接口)类型嵌入到结构(而不​​是接口)中,这“真正”创建了一个 A.b 字段。此外,嵌入类型的字段和方法被提升到嵌入器,就好像它们是嵌入器的字段或方法一样。引用自 Spec: Struct types:

A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

定义包外的代码不能引用嵌入字段A.b,这是正确的,但是定义包外的代码可以调用提升的A.G() 方法,因为这个标识符不是 lower-cased,所以限制不适用于它。

non-exported 标识符的限制是在您尝试引用它们时由编译器强制执行的。从另一个包写入 a.b.G() 是一个 compile-time 错误,因为您指的是 non-exported 标识符 (a.b)。当您写 a.G() 时,您没有引用任何 non-exported 标识符,仅引用导出的 a.G 标识符,因此它是允许的。