golang 值接收者和指针接收者

代码示例

先看个代码:

package main

import (
	"fmt"
	"testing"
)

func TestValRecv(t *testing.T) {
	var a animal = dog{"五红犬"}
	a.eat()
	a.sleep()

	var b animal = &dog{"五黑犬"}
	b.eat()
	b.sleep()
}

func TestPtrRecv(t *testing.T) {
	var a animal = &cat{"蓝猫"}
	a.eat()
	a.sleep()

	var b animal = cat{"胖橘"} // sleep only a ptr recv,出错
	b.eat()
	b.sleep()
}

type animal interface {
	eat()
	sleep()
}

type dog struct {
	name string
}

func (d dog) eat()  {
	fmt.Printf("%s eat...\n", d.name)
}

func (d dog) sleep()  {
	fmt.Printf("%s sleep...\n", d.name)
}

type cat struct {
	name string
}

func (d cat) eat()  {
	fmt.Printf("%s eat...\n", d.name)
}

func (d *cat) sleep()  {
	fmt.Printf("%s sleep...\n", d.name)
}

先说结论:

  • 值接收者,默认实现了值接收和指针接收方法,源对象的一个复制或者拷贝
  • 指针接收,只实现指针接收,重点是修改原对象

所以上面 cat 一个是 eat() 值接收方法,实现了二者,而 sleep() 指针接收,只实现了指针接收,未完全实现接口 animal 的两个方法。

如果不涉及接口,二者都可以,但涉及到接口涉及具体实现问题。

对于接口

结论:
实现了接收者是值类型的接口方法,相当于自动实现了接收者是指针类型的方法;
实现了接收者是指针类型的接口方法,不会自动生成对应接收者是值类型的方法。

简单的解释

接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。
所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原
本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。

两者分别在何时使用

如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。

使用指针作为方法的接收者的理由:
方法能够修改接收者指向的值。避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。

使用值接收者还是指针接收者,不是由该方法是否修改了调用者(也就是接收者)来决定,而是应该基于该类型的本质。

如果类型具备“原始的本质”,也就是说它的成员都是由 Go 语言里内置的原始类型,如字符串,整型值等,那就定义值接收者类型的方法。像内置的引用类型,如 slice,map,interface,channel,这些类型比较特殊,声明他们的时候,实际上是创建了一个 header, 对于他们也是直接定义值接收者类型的方法。这样,调用函数时,是直接 copy 了这些类型的 header,而 header 本身就是为复制设计的。

如果类型具备非原始的本质,不能被安全地复制,这种类型总是应该被共享,那就定义指针接收者的方法。比如 go 源码里的文件结构体(struct File)就不应该被复制,应该只有一份实体。

参考:

posted on 2024-07-22 16:17  进击的davis  阅读(1)  评论(0编辑  收藏  举报

导航