为什么隐式非指针方法不满足接口?
Why implicit non-pointer methods not satisfy interface?
Assuming we have an understanding that,
For explicit method definition for type X
, GO compiler implicitly defines the same method for type *X
and vice versa, if I declare,
func (c Cat) foo(){
//do stuff_
}
and declare,
func (c *Cat) foo(){
// do stuff_
}
then GO compiler gives error,
Compile error: method re-declared
which indicates that, pointer method is implicitly defined and vice versa
在下面的代码中,
package main
type X interface{
foo();
bar();
}
type Cat struct{
}
func (c Cat) foo(){
// do stuff_
}
func (c *Cat) bar(){
// do stuff_
}
func main() {
var c Cat
var p *Cat
var x X
x = p // OK; *Cat has explicit method bar() and implicit method foo()
x = c //compile error: Cat has explicit method foo() and implicit method bar()
}
GO 编译器报错,
cannot use c (type Cat) as type X in assignment:
Cat does not implement X (bar method has pointer receiver)
在x = c
,因为,隐式指针方法满足接口,但隐式非指针方法不满足。
问题:
为什么隐式非指针方法不满足接口?
让我们看看语言 specification:
A type may have a method set associated with it. The method set
of an interface type is its interface. The method set of any other
type T consists of all methods declared with receiver type T. The
method set of the corresponding pointer type *T is the set of all
methods declared with receiver *T or T (that is, it also contains
the method set of T).
在你的例子中,接口类型x
的方法集是[foo(), bar()]
。 Cat
类型的方法集为[foo()]
,*Cat
类型的方法集为[foo()]
+ [bar()]
= [foo(), bar()]
。
这解释了,为什么变量 p
满足接口 x
,但变量 c
不满足。
方法设置
继spec之后:
The method set of any other named type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).
在您遵循可寻址和不可寻址类型概念之前,方法集定义听起来很奇怪。
可寻址和不可寻址类型
如果值是可寻址类型,则可以调用指针接收器方法。
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.
在处理可寻址类型(结构是可寻址的)之前,可以调用值的指针接收器方法:
type Cat struct {}
func (c *Cat) bar() string { return "Mew" }
func main() {
var c Cat
c.bar()
}
接口类型变量不可寻址
但并非所有 Go 类型都是可寻址的。此外 通过接口引用的变量不可寻址。
无法在不可寻址类型的值上调用指针接收器:
type X interface {
bar() string
}
type Cat struct{}
func (c *Cat) bar() string { return "Mew" }
/* Note `cat` variable is not a `struct` type value but
it is type of `X` interface therefor it is not addressable. */
func CatBar(cat X) {
fmt.Print(cat.bar())
}
func main() {
var c Cat
CatBar(c)
}
因此出现以下错误 Go 运行时可防止段错误:
cannot use c (type Cat) as type X in assignment:
Cat does not implement X (bar method has pointer receiver)
为 dev.bmax 的回答添加一点内容。
type Cat struct{
}
func (c Cat) foo(){
// do stuff_
}
func (c *Cat) bar(){
// do stuff_
}
你可以做到
var c cat
c.bar() // ok to call bar(), since c is a variable.
但不是
cat{}.bar() // not ok to call bar(), c is not a variable.
只要参数是变量,就可以对类型 T 的参数调用 *T 方法;编译器隐式地获取它的地址。但这只是语法糖:类型 T 的值不拥有 *T 指针所拥有的所有方法,因此它可能满足更少的接口。
另一方面,您始终可以使用 Cat 或 *Cat 调用 foo()。
这个怎么样?
package main
import (
"fmt"
)
type Growler interface{
Growl() bool
}
type Cat struct{
Name string
Age int
}
// *Cat is good for both objects and "references" (pointers to objects)
func (c *Cat) Speak() bool{
fmt.Println("Meow!")
return true
}
func (c *Cat) Growl() bool{
fmt.Println("Grrr!")
return true
}
func main() {
var felix Cat // is not a pointer
felix.Speak() // works :-)
felix.Growl() // works :-)
var ginger *Cat = new(Cat)
ginger.Speak() // works :-)
ginger.Growl() // works :-)
}
Assuming we have an understanding that,
For explicit method definition for type
X
, GO compiler implicitly defines the same method for type*X
and vice versa, if I declare,func (c Cat) foo(){ //do stuff_ }
and declare,
func (c *Cat) foo(){ // do stuff_ }
then GO compiler gives error,
Compile error: method re-declared
which indicates that, pointer method is implicitly defined and vice versa
在下面的代码中,
package main
type X interface{
foo();
bar();
}
type Cat struct{
}
func (c Cat) foo(){
// do stuff_
}
func (c *Cat) bar(){
// do stuff_
}
func main() {
var c Cat
var p *Cat
var x X
x = p // OK; *Cat has explicit method bar() and implicit method foo()
x = c //compile error: Cat has explicit method foo() and implicit method bar()
}
GO 编译器报错,
cannot use c (type Cat) as type X in assignment:
Cat does not implement X (bar method has pointer receiver)
在x = c
,因为,隐式指针方法满足接口,但隐式非指针方法不满足。
问题:
为什么隐式非指针方法不满足接口?
让我们看看语言 specification:
A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).
在你的例子中,接口类型x
的方法集是[foo(), bar()]
。 Cat
类型的方法集为[foo()]
,*Cat
类型的方法集为[foo()]
+ [bar()]
= [foo(), bar()]
。
这解释了,为什么变量 p
满足接口 x
,但变量 c
不满足。
方法设置
继spec之后:
The method set of any other named type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).
在您遵循可寻址和不可寻址类型概念之前,方法集定义听起来很奇怪。
可寻址和不可寻址类型
如果值是可寻址类型,则可以调用指针接收器方法。
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.
在处理可寻址类型(结构是可寻址的)之前,可以调用值的指针接收器方法:
type Cat struct {}
func (c *Cat) bar() string { return "Mew" }
func main() {
var c Cat
c.bar()
}
接口类型变量不可寻址
但并非所有 Go 类型都是可寻址的。此外 通过接口引用的变量不可寻址。
无法在不可寻址类型的值上调用指针接收器:
type X interface {
bar() string
}
type Cat struct{}
func (c *Cat) bar() string { return "Mew" }
/* Note `cat` variable is not a `struct` type value but
it is type of `X` interface therefor it is not addressable. */
func CatBar(cat X) {
fmt.Print(cat.bar())
}
func main() {
var c Cat
CatBar(c)
}
因此出现以下错误 Go 运行时可防止段错误:
cannot use c (type Cat) as type X in assignment: Cat does not implement X (bar method has pointer receiver)
为 dev.bmax 的回答添加一点内容。
type Cat struct{
}
func (c Cat) foo(){
// do stuff_
}
func (c *Cat) bar(){
// do stuff_
}
你可以做到
var c cat
c.bar() // ok to call bar(), since c is a variable.
但不是
cat{}.bar() // not ok to call bar(), c is not a variable.
只要参数是变量,就可以对类型 T 的参数调用 *T 方法;编译器隐式地获取它的地址。但这只是语法糖:类型 T 的值不拥有 *T 指针所拥有的所有方法,因此它可能满足更少的接口。
另一方面,您始终可以使用 Cat 或 *Cat 调用 foo()。
这个怎么样?
package main
import (
"fmt"
)
type Growler interface{
Growl() bool
}
type Cat struct{
Name string
Age int
}
// *Cat is good for both objects and "references" (pointers to objects)
func (c *Cat) Speak() bool{
fmt.Println("Meow!")
return true
}
func (c *Cat) Growl() bool{
fmt.Println("Grrr!")
return true
}
func main() {
var felix Cat // is not a pointer
felix.Speak() // works :-)
felix.Growl() // works :-)
var ginger *Cat = new(Cat)
ginger.Speak() // works :-)
ginger.Growl() // works :-)
}