【问题标题】:Visibility of embedded private interfaces in GoGo 中嵌入式私有接口的可见性
【发布时间】:2018-08-01 01:56:13
【问题描述】:

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

type b interface {
    G() int
}

type A interface {
    F() string
    b
}

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

【问题讨论】:

  • 你也试过吗?

标签: go interface visibility embedding


【解决方案1】:

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

接口嵌入接口无非是将嵌入接口的method set合并到嵌入器中,因此成为嵌入器类型的方法集的一部分。是否导出嵌入式接口类型都没有关系。引用自Spec: Interface types:

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

在您的代码中发生的所有事情是 A 将是一个具有 2 个方法的接口:F() stringG() 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标识符,所以是允许的。

【讨论】:

  • 这真的很有趣。我原以为G 会因为“落后”b 而被隐藏起来。谢谢。
  • "但如果是(连同A.b 字段),a.b.G() 将被称为没有问题。" - 你在这里是什么意思。不清楚
  • @aerokite 措辞不完全正确,编辑它。我的意思是在我的例子中anil。如果你给它分配一个非nil 的值(实现subplay.A),那么a.G() 调用就不会恐慌。请参阅编辑后的答案。
  • @Ralph 完全修改了我的答案,请检查新版本。
  • 如果由于两个具有相同名称的嵌入式私有类型而存在歧义,则可以在定义包之外引用公共提升值这一事实可能会出现问题。感谢您的详尽回答。
猜你喜欢
  • 1970-01-01
  • 2013-06-13
  • 1970-01-01
  • 2013-02-04
  • 2021-02-10
  • 2023-03-07
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多