5

假设我们有一个理解,

对于 type 的显式方法定义X,GO 编译器为 type 隐式定义相同的方法,*X反之亦然如果我声明,

func (c Cat) foo(){
  //do stuff_
} 

并声明,

func (c *Cat) foo(){
  // do stuff_
}

然后 GO 编译器给出错误,

Compile error: method re-declared

这表明,指针方法是隐式定义的,反之亦然


在下面的代码中,

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)

at x = c,因为隐式指针方法满足接口,但隐式非指针方法不满足。

问题:

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

4

4 回答 4

6

让我们看看语言规范

一个类型可能有一个与之关联的方法集。接口类型的方法集就是它的接口。任何其他类型 T 的方法集由所有用接收器类型 T 声明的方法组成。对应指针类型 *T 的方法集是用接收器 *T 或 T 声明的所有方法的集合(即它还包含该方法T组)。

在您的示例中,接口类型的方法集x[foo(), bar()]. 该类型的方法集Cat[foo()],该类型的方法集*Cat[foo()]+ [bar()]= [foo(), bar()]

这解释了为什么变量p满足接口x,但变量c不满足。

于 2017-01-29T15:28:01.557 回答
1

方法集

遵循规范

任何其他命名类型 T 的方法集由所有接收者类型为 T 的方法组成。对应指针类型 *T 的方法集是所有接收者为 *T 或 T 的方法的集合(即它还包含方法集T)。

在您遵循可寻址和不可寻址类型概念之前,方法集定义听起来很奇怪。

可寻址和不可寻址类型

如果值是可寻址类型,则可以对值调用指针接收器方法。

与选择器一样,使用指针对具有值接收器的非接口方法的引用将自动取消对该指针的引用:pt.Mv 等价于 (*pt).Mv。

与方法调用一样,使用可寻址值的指针接收器对非接口方法的引用将自动获取该值的地址:t.Mp 等价于 (&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 运行时会出现以下错误,从而防止段错误:

不能在赋值中使用 c(Cat 类型)作为 X 类型:Cat 没有实现 X(bar 方法有指针接收器)

于 2017-01-29T15:37:48.997 回答
0

这个怎么样?

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 :-)
}
于 2017-01-29T16:59:33.927 回答
0

在 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()。

于 2017-01-29T15:40:26.560 回答