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

时间:2017-01-29 14:49:04

标签: go methods interface structural-typing

  

假设我们理解,

     

对于类型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,因为,隐式指针方法满足接口,但隐式非指针方法则不然。

问题:

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

4 个答案:

答案 0 :(得分:3)

让我们看看语言specification

  

类型可能与方法集相关联。方法集   接口类型是它的接口。任何其他方法集   type T包含用接收器类型T声明的所有方法   对应指针类型的方法集* T是所有的集合   用接收器* T或T声明的方法(也就是说,它也包含   T)的方法集

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

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

答案 1 :(得分:1)

这个怎么样?

// simulate modal opening
$('.nav-link').click(function(e) {
  if ($(window).width() > 992) {
    $('.backdrop').hide(0, false);
  }
  
	$('#navToggle').click();
});

$('.navmenu').on('show.bs.offcanvas', function() {
  if ($(window).width() <= 992) {
    $('.backdrop').fadeIn();
  }
});

$('.navmenu').on('hide.bs.offcanvas', function() {
  if ($(window).width() <= 992) {
    $('.backdrop').fadeOut();
  }
});


// close modal on resize
$(window).resize(function() {
  if ($(window).width() > 992) {
    $('.backdrop').hide(0, false);
    $('body').removeClass('bs.offcanvas');
  }
});

// switch active navigation link onclick
$('.nav a').on('click', function() {
  $('.nav').find('.active').removeClass('active');
  $(this).parent().addClass('active');
});

// close Modal when navigating to anchor
$('.navmenu-nav li a').on('click', function() {
  $('.backdrop').hide(0, false);
  $('body').removeClass('bs.offcanvas');
});

答案 2 :(得分:0)

方法集

关注spec

  

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

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

可寻址且不可寻址的类型

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

  

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

     

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

答案 3 :(得分: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()