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

Go 为什么隐式非指针方法不满足接口?,go,methods,interface,structural-typing,Go,Methods,Interface,Structural Typing,假设我们了解到 对于类型X的显式方法定义,如果我声明 func (c Cat) foo(){ //do stuff_ } 并宣布, func (c *Cat) foo(){ // do stuff_ } 然后去编译器给出错误 Compile error: method re-declared 这表明,指针方法是隐式定义的,反之亦然 在下面的代码中 package main type X interface{ foo(); bar(); } type Cat stru

假设我们了解到

对于类型
X
的显式方法定义,如果我声明

func (c Cat) foo(){
  //do stuff_
} 
并宣布,

func (c *Cat) foo(){
  // do stuff_
}
然后去编译器给出错误

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)
x=c
,因为隐式指针方法满足接口,而隐式非指针方法不满足接口

问题:


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

让我们看看这种语言:

类型可能有与其关联的方法集。方法集 接口类型的名称是其接口。任何其他方法的方法集 类型T由使用接收方类型T声明的所有方法组成 对应指针类型的方法集*T是所有 用receiver*T或T声明的方法(也就是说,它还包含 方法集(T)

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

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

方法集 以下是:

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

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

可寻址和不可寻址类型 如果值为可寻址类型,则可以对该值调用指针接收器方法

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

与方法调用一样,使用可寻址值的指针接收器对非接口方法的引用将自动获取该值的地址:t.Mp相当于(&t).Mp

在处理可寻址类型(struct是可寻址的)之前,可以对值调用指针接收器方法:

接口类型的变量不可寻址 但并非所有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(条形方法具有指针接收器)


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

@凯文沃利斯:我必须再做一次改变。所以在你的上面编辑。你现在可以控制它了:)可能是的复制品。同样相关:.@icza在该查询分配中由于未实现接口而失败,此处
x=p
未失败您正在用假想的隐式方法定义混淆人们。@overexchange,t不是隐式定义,只是指针具有访问权限(实际上是访问权限)指向值,但值无权访问指针。值receiver受此限制。1)还包含方法集T,mean,如果receiver type(
*Cat
)定义了方法(
bar()
),则编译器为receiver type(
Cat
)创建方法
bar()
的隐式定义,我在查询的第一部分以及代码中的问题行中都提到了这一点(带注释)。2)对于您的行
x=&c
,您是说,
&c
属于
*Cat
类型,并且
*Cat
类型具有满意的接口,我在查询中也这样说,因此works@overexchange,“t”是丢失“i”=)的“it”,请检查第24行的(
c.bar()
)并解释我,谁为类型(
Cat
)定义了方法
bar()。对于类型
Cat
,没有方法
bar()
。但是由于
c
是可寻址的,为了方便起见,编译器将调用
c.bar()
转换为
(&c.bar()
)。同样,如果
p
*Cat
,编译器将
p.foo()
转换为
(*p.foo()
)。没有隐式方法,只有隐式重写调用。
c.bar()
的工作原理如图所示。那么,您如何证明类型(
Cat
)的方法集是
[foo()]
c.bar()
,因为编译器将此语句替换为
(&c.bar()
)。但是,
c
不能被分配到接口
x
,因为对于它自己的类型
Cat
,没有像
bar()
这样的方法。对于非指针,
var c Cat
,你将如何调用
(*c).bar()
?这是一个打字错误,已修复。根据规范,在这方面(
*Cat
)的方法集是什么?为什么
p.bar()
失败了?根据您的观点,为什么不调用
(*p).bar()
来代替
p.bar()
felix.Speak()
felix.Growl()
有效,因为正如我在查询中所说的,对于显式的我
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 :-)
}