探讨一下 receiver 和隐式解引用和取址
接口
上篇说道,Go 语言是的接口是隐式实现的,但选择值接收者还是指针接收者是有讲究的。
先来看看接口的定义:
type iface struct {
tab *itab // 类型信息
data unsafe.Pointer //实际对象指针
}
type itab struct {
inter *interfacetype // 接口类型
_type *_type // 实际对象类型
fun [1]uintptr // 实际对象方法地址
}
也就是说,接口由类型信息和方法集组成。在将一个对象赋值给某个方法集时,实际上是做了上述的转换和赋值。
由于值接收者是一个值,指针接收者是指针,两者是不同的类型,所包含的方法集是不同的。简单来说:
T 类型包含 receiver T 的方法集
*T 类型包含 receiver T + receiver *T 的方法集
隐式解引用和取址
对于复杂指针类型(结构体),会自动进行解引用操作,如
p1 := &Person{name: "易天", age: 24}
fmt.Println((*p1).name)
fmt.Println(p1.name)
因此,当某个 struct 实现了某个方法,其值或者指针类型可以随意调用,T 可以调用 receiver T 和 receiver *T 的方法,反之亦然。
package main
import (
"fmt"
)
type Ball struct {
Name string
}
func (b *Ball) Ping() {
fmt.Println("ping")
fmt.Printf("%p\n", b)
}
func (b Ball) Pong() {
fmt.Println("pong")
fmt.Printf("%p\n", b)
}
func main() {
v := Ball{}
p := &Ball{}
v.Ping()
fmt.Printf("%p\n", &v)
v.Pong()
p.Ping()
p.Pong()
}
总结
- 对于接口来说,receiver 的类型有重要意义,这是判断 T 或者 *T 有无实现该接口的重要依据,只有实现了该接口所有的方法,才能进行赋值。
- 隐式解引用和取址是 Go 的一个特性,这点和 receiver 的方法集要区分开。