我是新手gopher,并试图让我的头围绕指针接收器和接口.
type Foo interface {
foo()
}
type Bar struct {}
func (b *Bar) foo() {}
Run Code Online (Sandbox Code Playgroud)
基于以上定义..
--- Allowed ---------
b := Bar{}
b.foo()
--- Not allowed -----
var foo Foo = Bar{}
Run Code Online (Sandbox Code Playgroud)
获取编译器错误:不能在赋值时使用Bar literal(类型Bar)作为类型Foo:Bar不实现Foo(foo方法有指针接收器)
我知道编译器在第一个场景中代表我们做了一些指针转换和解引用.为什么不在第二种情况下做同样的事情?
解释在于,当处理具体结构本身时,它具有适当的信息来自动处理它。您可以在此处的导览中读到:
Go 自动处理方法调用的值和指针之间的转换。
但是,当您处理类型时interface{},它对变量中实际包含的内容的信息较少。它只知道有一个foo()方法。但这里有一个微妙之处,需要额外的解释,所以这里有一个例子。
https://play.golang.org/p/Y0fJcAISw1
type Foo interface {
foo()
}
type Bar struct {}
func (b *Bar) foo() {}
type Baz struct {}
func (b Baz) foo() {}
func main() {
b := Bar{}
b.foo()
var v Foo = &Bar{}
// v = Bar{} // fails
v.foo()
v = Baz{}
v.foo()
v = &Baz{} // works too
v.foo()
}
Run Code Online (Sandbox Code Playgroud)
请注意,&Baz{}即使它有一个值接收器,它仍然有效,但反之则不然。原因是 a*Baz恰好指向1 Baz,两者都存在(指针和值),因此很容易获得该值。当你尝试执行 时v = Bar{},值存在,但指针不存在,Go 不会自动为某个interface{}值创建一个指针。
这一切都在本博客文章的指针和接口标题下进行了详细解释
简短回答var foo Foo = Bar{}不起作用,因为存储在接口中的具体值不可寻址.
版本更长
请阅读https://github.com/golang/go/wiki/MethodSets
在已经是指针或可以采用其地址的任何东西上调用指针值方法是合法的.在值为或可以取消引用其值的任何值上调用值方法是合法的.
关于上面的解释,你的代码
b := Bar{}
b.foo()
Run Code Online (Sandbox Code Playgroud)
因为b可以解决而起作用.
存储在接口中的具体值不可寻址.因此,当您在接口上调用方法时,它必须具有相同的接收器类型,或者必须可以直接从具体类型中识别:指针和值接收器方法可以分别使用指针和值调用,如您所料.可以使用指针值调用值接收器方法,因为它们可以首先解除引用.但是,不能使用值调用指针接收器方法,因为存储在接口内的值没有地址.在为接口分配值时,编译器会确保实际上可以在该值上调用所有可能的接口方法,因此尝试进行不正确的分配将在编译时失败.
根据上面的解释,存储在接口中的具体值是不可寻址的,因此代码,
var foo Foo = Bar{}
Run Code Online (Sandbox Code Playgroud)
因为存储在接口中的具体值在这种情况下Bar{}是不可寻址的,所以不起作用.