问题提要

之前写代码的时候遇到了一个问题:自己编写了一个接口,然后又写了一个结构体实现这个接口,在通过函数调用接口方法时出现了问题。
代码如下:

type Validator interface  {
  Valid() bool
}

type LoginInput struct {
  Username string
  Password string
}

func (input *LoginInput) Valid() bool {
  // 一些检验逻辑
  // 返回校验结果
}

func Handle(v Validator) {
  res := v.Valid()
  // 根据校验结果做一些逻辑处理
}

func main() {
  // 对具体过程做了提炼,最终逻辑一致
  input := LoginInput{Username: "XXX", Password: "YYY"}
  Handle(input)
}
mainHandle()GolandCannot use 'input' (type LoginInput) as type ValidatorType does not implement 'Validator' as 'Valid' method has a pointer receiver
Handle()Handle(&input)
方法集

什么是方法集

我们先来看看Golang官方对它的描述:

总结一下官方文档表述的意思,我们得到如下一张表:

变量类型方法接收器类型
T(t T)
*T(t T) + (t *T)
TT*TT*TLoginInputnotify

结构体的方法调用与方法集之间的关系

其实到这里就会有个疑问:平时调用方法时,无论变量类型是值类型还是指针类型都能调用成功,也没出过问题啊。
这里其实是Golang的一个语法糖:在使用选择器(Selectors)调用方法时,编译器会帮你做好取址或取值的操作的。
下面通过代码说明一下这个关系:

type StructA struct {}

func (s StructA) ValueReceiver () {}

func (s *StructA) PointReceiver () {}

func main() {
  value := StructA{}
  point := &value
  // 编译器不做处理,就是value.ValueReceiver()
  value.ValueReceiver()
  // 其实是(&value).ValueReceiver()的简便写法
  value.PointReceiver()
  // 其实是(*point).ValueReceiver()的简便写法
  point.ValueReceiver()
  // 编译器不做处理,就是point.ValueReceiver()
  point.PointReceiver()
}