简体   繁体   English

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

[英]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, 对于类型X显式方法定义,GO编译器隐式为类型*X定义相同的方法, 反之亦然 ,如果我声明,

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

and declare, 并宣布,

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

then GO compiler gives error, 然后GO编译器给出错误,

 Compile error: method re-declared 

which indicates that, pointer method is implicitly defined and vice versa 这表明,指针方法是隐式定义的, 反之亦然


In the below code, 在下面的代码中,

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 compiler gives error, GO编译器出错,

cannot use c (type Cat) as type X in assignment:
    Cat does not implement X (bar method has pointer receiver)

at x = c , because, implicit pointer methods satisfy interfaces, but implicit non-pointer methods do not. x = c ,因为,隐式指针方法满足接口,但隐式非指针方法不满足。

Question: 题:

Why implicit non-pointer methods do not satisfy interfaces? 为什么隐式非指针方法不满足接口?

Let's look into the language 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). 任何其他类型T的方法集由用接收器类型T声明的所有方法组成。相应指针类型* T的方法集是用receiver * T或T声明的所有方法的集合(也就是说,它还包含方法一套 T)。

In your example, the method set of the interface type x is [foo(), bar()] . 在您的示例中,接口类型x的方法集是[foo(), bar()] The method set of the type Cat is [foo()] , and the method set of the type *Cat is [foo()] + [bar()] = [foo(), bar()] . Cat类型的方法集是[foo()]*Cat类型的方法集是[foo()] + [bar()] = [foo(), bar()]

This explains, why variable p satisfies the interface x , but variable c doesn't. 这解释了为什么变量p满足接口x ,但变量c不满足。

How about this? 这个怎么样?

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

Method set 方法集

Following the 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). 任何其他命名类型T的方法集由具有接收器类型T的所有方法组成。相应指针类型* T的方法集是具有接收器* T或T的所有方法的集合(即,它还包含方法集) T)。

Method set definition sounds weird until you follow addressable and not addressable types concept. 方法集定义听起来很奇怪,直到你遵循可寻址和不可寻址类型的概念。

Addressable and not addressable types 可寻址和不可寻址的类型

It is possible to call a pointer receiver method on a value if the value is of addressable type. 如果值是可寻址类型,则可以对值调用指针接收器方法。

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. 与选择器一样,使用指针对带有值接收器的非接口方法的引用将自动取消引用该指针:pt.Mv等效于(* 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. 与方法调用一样,使用可寻址值对带有指针接收器的非接口方法的引用将自动获取该值的地址:t.Mp等效于(&t).Mp。

It is ok to call pointer receiver methods on values till you are dealing with addressable types (struct is addressable): 可以在值上调用指针接收器方法,直到处理可寻址类型(struct是可寻址的):

type Cat struct {}

func (c *Cat) bar() string { return "Mew" }

func main() {
    var c Cat
    c.bar()
}

Variables of interface type are not addressable 接口类型的变量不可寻址

But not all Go types are addressable. 但并非所有Go类型都是可寻址的。 Also variables referenced through interfaces are not addressable . 通过接口引用的变量也是不可寻址的

It is impossible to call pointer receiver on values of not addressable types: 在不可寻址类型的值上调用指针接收器是不可能的:

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

So with the following error Go runtime prevents segment fault: 因此,使用以下错误Go运行时可防止段错误:

cannot use c (type Cat) as type X in assignment: Cat does not implement X (bar method has pointer receiver) 不能在赋值时使用c(类型Cat)作为类型X:Cat不实现X(bar方法有指针接收器)

Add a little to dev.bmax's answer. 添加一点dev.bmax的答案。

type Cat struct{
}

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

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

you can do 你可以做

var c cat
c.bar() // ok to call bar(), since c is a variable.

but not 但不是

cat{}.bar() // not ok to call bar(), c is not a variable.

It's legal to call a *T method on an argument of type T so long as the argument is a variable ; 只要参数是变量 ,在类型T的参数上调用* T方法是合法的。 the compiler implicitly takes its address. 编译器隐式地获取其地址。 But this is mere syntactic sugar: a value of type T does not posses all methods that a *T pointer does, and as a result it might satisfy fewer interfaces. 但这仅仅是语法糖:类型T的值不具有* T指针所做的所有方法,因此它可能满足更少的接口。

On the other hand, you can always call foo() with Cat or *Cat. 另一方面,您始终可以使用Cat或* Cat调用foo()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM