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
  }

 

posted @ 2023-10-15 23:59  _zxq  阅读(49)  评论(0编辑  收藏  举报