golang的值接收者与指针接收者(二)

发布时间 2023-10-16 00:02:24作者: _zxq

虽然在平时使用时,值接收者和指针接收者调用值方法和指针方法都没有问题,但是在涉及到实现接口方法时,有时会遇到如下报错:

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只包含了接收者为Student的SetAge方法,而不包含接收者为*Student的SetAge2方法。所以,st1这个Student{}类型的接收者没有继承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
  }