我是新手gopher,并试图让我的头围绕指针接收器和接口 .
type Foo interface {
foo()
}
type Bar struct {}
func (b *Bar) foo() {}
基于以上定义..
--- Allowed ---------
b := Bar{}
b.foo()
--- Not allowed -----
var foo Foo = Bar{}
获取编译器错误:不能在赋值时使用Bar literal(类型Bar)作为类型Foo:Bar不实现Foo(foo方法有指针接收器)
我理解编译器在第一个场景中代表我们做了一些指针转换和解引用 . 为什么不在第二种情况下做同样的事情?
3 回答
简短回答
var foo Foo = Bar{}
无效,因为存储在接口中的具体值无法寻址 .Longer Version
请阅读https://github.com/golang/go/wiki/MethodSets
关于上面的解释,你的代码
有效,因为
b
是可寻址的 .根据上面的解释,存储在接口中的具体值是不可寻址的,因此代码,
因为存储在接口中的具体值(在本例中为
Bar{}
)不可寻址,所以不起作用 .解释在于,当处理具体结构本身时,它具有自动处理它的适当信息 . 你可以阅读in the tour here:
但是当您处理
interface{}
类型时,它对变量中实际包含的内容的信息较少 . 它只知道有一个foo()
方法 . 但这里有一个微妙的需要额外的解释所以这里是一个例子 .https://play.golang.org/p/Y0fJcAISw1
请注意,
&Baz{}
即使具有值接收器也能正常工作,但不是相反 . 原因是*Baz
完全指向 oneBaz
,两者都存在(指针和值),因此很容易获得该值 . 当您尝试执行v = Bar{}
时,该值存在,但指针不存在,并且Go不会自动为interface{}
值创建一个值 .这一切都在 Pointers and interfaces Headers in this blog post下详细解释
您的问题的一半取决于您的 Value 是否可寻址:
Bar{}
是复合文字,因此无法寻址 . 您可以键入&Bar{}
来创建*Bar
类型的对象,但它被列为"an exception to the addressability requirement",这强化了Bar{}
本身不可寻址的想法 .Bar
类型的变量b
可以调用b.foo()
尽管Bar.foo()
需要指针接收器,原因很简单:但是,这并不意味着
Bar.foo()
位于b
的方法集中 . 这是因为b
的类型为Bar
,而Bar.foo()
的类型为*Bar
:因为
b
和Foo
接口的方法集不同,所以尽管编译器将b.foo()
转换为(&b).foo()
,但不能使用var foo Foo = b
. 否则,var foo Foo = Bar{}
会起作用 . 但是,您可以使用以下任一项,因为Bar.foo()
收到*Bar
: