golang的值接收者与指针接收者(二)
虽然在平时使用时,调用者不论是值类型还是指针类型,调用值接收者方法和指针接收者方法都没有问题,但是在涉及到实现接口方法时,有时会遇到如下报错:
Son does not implement Father (SomeFunction method has pointer receiver)
比如:
type Person interface { SetAge(age int) SetAge2(age int) } type Student struct { age int } func (s Student) SetAge(age int) { s.age = age } func (s *Student) SetAge2(age int) { s.age = age } func main() { var st1 Person = Student{} var st2 Person = &Student{} st1.SetAge(11) fmt.Println("st11:", st1) st1.SetAge2(12) fmt.Println("st12:", st1) st2.SetAge(21) fmt.Println("st21:", st2) st2.SetAge2(22) fmt.Println("st22:", st2) }
编译直接报错了:
cannot use Student{} (value of type Student) as type Person in variable declaration: Student does not implement Person (SetAge2 method has pointer receiver)
意思是Student{}没有实现Person的接口,后面括号指出是SetAge2接口没有实现。
先说结论:*T类型包含了*T和T为接收者的方法,但T类型只包含接收者为T的方法。(参考:https://go.dev/doc/faq#Functions_methods)
即,上面的例子中,值类型st1只包含了值类型接收者方法(即SetAge方法),而不包含指针类型接收者方法(即SetAge2方法)。所以,st1这个值类型结构体没有能够完全实现Person接口。
而在前一篇文章的使用例子中,值或者指针调用者之所以可以直接调用不同接收者类型的方法,完全是编译器帮忙做了一些工作:我们的值类型调用者在调用指针类型接收者的方法的时候,会对调用者取址处理来调用其方法,即&st1.SetAge2()。所以,这个调用者能不能调用指针接收者类型的方法,取决于调用者能不能被寻址。而接口的动态值,就是一种不能被寻址的类型。
其他的不能寻址的类型,比如常量:
type Pi int func (p *Pi) SetValue(age int) { *p = Pi(age) } func (p Pi) SetValue2(age int) { p = Pi(age) } func main() { const pi Pi = 123 pi.SetValue(1) // 编译报错:cannot call pointer method SetValue on Pi pi.SetValue2(2) // ok var pi2 Pi = 3 pi2.SetValue(3) // ok pi2.SetValue2(4) // ok }