对 interface 赋值变量的时候经常出的问题

日常开发时有个这样的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

package main

import "fmt"

type Human interface {
	Run()
}

type Boy struct {
	Name string
}

func (b *Boy) Run() {
	fmt.Println("my name is " + b.Name)
}

func main() {
	xiaoming := Boy{Name: "xiao ming"}
	var h Human
	// h = xiaoming // #1 会报错, Boy does not implement Human (Run method has pointer receiver)
	h = &xiaoming
	h.Run()
}

如果用 #1 这行代码时会出现以下错误:

1
2
3
# command-line-arguments
./main.go:23:4: cannot use xiaoming (type Boy) as type Human in assignment:
        Boy does not implement Human (Run method has pointer receiver)

然而用下面的 h = &xiaoming 是正常的

这个问题,首先得先了解一下Golang 中 方法的集合的概念,一个struct虽然可以通过值类型和引用类型两种方式定义方法,但是不通的对象类型对应了不同的方法集:

1
2
3
4
Values                    Methods Receivers
-----------------------------------------------
 T                        (t T)
*T                        (t T) and (t *T) 

值类型的对象只有(t T) 结构的方法,虽然值类型的对象也可以调用(t *T) 方法,但这实际上是Golang编译器自动转化成了&t的形式来调用方法,并不是表明值类型的对象拥有该方法。

换一个维度来看上面的表格可能更加直观:

1
2
3
4
5
Methods Receivers         Values
-----------------------------------------------
(t T)                     T and *T

(t *T)                    *T 

这就意味着指针类型的receiver 方法实现接口时,只有指针类型的对象实现了该接口。

对应上面的例子来说,只有&user实现了notifier接口,而user根本没有实现该接口。所以上面代码会报出这样的异常。