繁体   English   中英

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

[英]Why implicit non-pointer methods not satisfy interface?

假设我们有一个理解,

对于类型X显式方法定义,GO编译器隐式为类型*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)

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

题:

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

让我们看一下语言规范

类型可以具有与其关联的方法集 接口类型的方法集是其接口。 任何其他类型T的方法集由用接收器类型T声明的所有方法组成。相应指针类型* T的方法集是用receiver * T或T声明的所有方法的集合(也就是说,它还包含方法一套 T)。

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

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

这个怎么样?

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

方法集

遵循规范

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

方法集定义听起来很奇怪,直到你遵循可寻址和不可寻址类型的概念。

可寻址和不可寻址的类型

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

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

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

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

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方法有指针接收器)

添加一点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()。

暂无
暂无

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

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