value receiver & pointer receiver

621 阅读1分钟

探讨一下 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()

}

总结

  1. 对于接口来说,receiver 的类型有重要意义,这是判断 T 或者 *T 有无实现该接口的重要依据,只有实现了该接口所有的方法,才能进行赋值。
  2. 隐式解引用和取址是 Go 的一个特性,这点和 receiver 的方法集要区分开。