使用值接收器的方法既可以通过值调用,也可以通过指针调用。

带指针接收器的方法只能通过指针或 addressable values调用.

例如,

type S struct {
  data string
}

func (s S) Read() string {
  return s.data
}

func (s *S) Write(str string) {
  s.data = str
}

sVals := map[int]S{1: {"A"}}

// 你只能通过值调用 Read
sVals[1].Read()

// 这不能编译通过:
//  sVals[1].Write("test")

sPtrs := map[int]*S{1: {"A"}}

// 通过指针既可以调用 Read,也可以调用 Write 方法
sPtrs[1].Read()
sPtrs[1].Write("test")

类似的,即使方法有了值接收器,也同样可以用指针接收器来满足接口.

type F interface {
  f()
}

type S1 struct{}

func (s S1) f() {}

type S2 struct{}

func (s *S2) f() {}

s1Val := S1{}
s1Ptr := &S1{}
s2Val := S2{}
s2Ptr := &S2{}

var i F
i = s1Val
i = s1Ptr
i = s2Ptr

//  下面代码无法通过编译。因为 s2Val 是一个值,而 S2 的 f 方法中没有使用值接收器
//   i = s2Val

Effective Go 中有一段关于 pointers vs. values 的精彩讲解。

补充:

  • 一个类型可以有值接收器方法集和指针接收器方法集
    • 值接收器方法集是指针接收器方法集的子集,反之不是
  • 规则
    • 值对象只可以使用值接收器方法集
    • 指针对象可以使用 值接收器方法集 + 指针接收器方法集
  • 接口的匹配(或者叫实现)
    • 类型实现了接口的所有方法,叫匹配
    • 具体的讲,要么是类型的值方法集匹配接口,要么是指针方法集匹配接口

具体的匹配分两种:

  • 值方法集和接口匹配
    • 给接口变量赋值的不管是值还是指针对象,都ok,因为都包含值方法集
  • 指针方法集和接口匹配
    • 只能将指针对象赋值给接口变量,因为只有指针方法集和接口匹配
    • 如果将值对象赋值给接口变量,会在编译期报错(会触发接口合理性检查机制)

为啥 i = s2Val 会报错,因为值方法集和接口不匹配.

最后编辑: kuteng  文档更新时间: 2021-05-09 20:12   作者:kuteng