一般来说,我们通常会在实施接口的时候,选择用值接收者,其实指定指针接收者也是允许的。但是当实施接口的时候使用指针接收者与使用值接收者是由细微差别的。
通过以下的示例来了解一下:
package main
import "fmt"
type Describer interface {
Describe()
}
type Person struct {
name string
age int
}
func (p Person) Describe() { //implemented using value receiver
fmt.Printf("%s is %d years old\n", p.name, p.age)
}
type Address struct {
state string
country string
}
func (a *Address) Describe() { //implemented using pointer receiver
fmt.Printf("State %s Country %s", a.state, a.country)
}
func main() {
var d1 Describer
p1 := Person{"Sam", 25}
d1 = p1
d1.Describe()
p2 := Person{"James", 32}
d1 = &p2
d1.Describe()
var d2 Describer
a := Address{"Washington", "USA"}
/* compilation error if the following line is
uncommented
cannot use a (type Address) as type Describer
in assignment: Address does not implement
Describer (Describe method has pointer
receiver)
*/
//d2 = a
d2 = &a //This works since Describer interface
//is implemented by Address pointer in line 22
d2.Describe()
}
从上面的代码可以看到,我们定义了两个结构体,并且让它们都实现了同样的接口,也就是Describer。在实施Address的接口的时候,指定的接收器是指针类型的。首先来看第一种情况,我们定义了两个Person类型的变量p1, p2,并且将它们分别赋值给接口类型变量d1, 前者使用了普通值,后者使用了指针值进行赋值,程序可以正常运行。
而到了第二种情况的时候,首先先定义一个Describer接口类型的变量d2, 然后我们直接将Address类型的值赋值给它,此时程序会产生异常, main.go:42: cannot use a (type Address) as type Describer in assignment: Address does not implement Describer (Describe method has pointer receiver). 而当我们将Address指针值赋值给变量时,程序不会报错,而是正常运行。在学习方法定义的时候,带有指针接收器的方法既可以接收值变量,也可以接收指针变量,但是为何这里会失败?
这是因为具有值接收器的方法接受指针和值接收器。在值是任何值或值可以被取消引用的任何值上调用值方法都是合法的。当我们在任何已经是具体的指针值或者可以被获取到地址的值上调用指针值接收器方法时都是合法的。将混合类型的值存储在接口类型的变量中时,无法被地址化,因此对于编译器来说它无法自动获取值的地址,所以代码失败了。