golang值接收者与指针接收者

3 阅读3分钟

golang函数方法的接收者有两种:值接收者与指针接收者。

平时使用中两种类型的主要区别就是能不能修改调用者的内部值。

先说结论:值接收者方法不能修改调用者内部的值,指针接收者方法可以修改调用者内部的值。

做个测试:

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() {
    st1 := Student{}
	st2 := &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)
}    
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() {
    st1 := Student{}
	st2 := &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)
}    

输出结果:

st11: {0}
st12: {12}
st21: &{0}
st22: &{22}

  可以看出,不论调用者是值还是指针,对于指针接收者方法和值接收者方法,都可以调用。而且指针接收者方法是可以修改调用者(结构体)的内部值,值接收者方法则不行。


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

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的方法。 (参考:go.dev/doc/faq#Fun…

即,上面的例子中,值类型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
  }

【迁移】www.cnblogs.com/zxq89/p/177… posted @ 2023-10-15 23:59