为什么隐式非指针方法不满足接口?

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