lqshow / notes

Cheat Sheet
10 stars 2 forks source link

Golang 中的方法和接收者 #46

Open lqshow opened 5 years ago

lqshow commented 5 years ago

方法和接收者

方法的声明

方法声明和函数声明只有一点差别,只是在关键字func方法名之间多加了一个参数。这个参数把这个方法绑定到这个参数对应的类型上。

类型拥有的所有方法名都必须是唯一的,因为每一个类型都有它自己的命名空间,所以不同类型可以使用相同的方法名。

方法的接收者

以下方法声明里的参数 t 就是这个方法的接收者

func (t *Type) Method() {} // pointer receiver
func (t Type) Method() {} // value receiver

pointer receiver

指针接收者,接收者的类型是一个指针,是接收者的引用,对这个引用的修改之间影响真正的接收者

value receiver

值接收者,接收者的类型是一个值,传递给方法的实际上是值的一个副本,方法内部无法对其真正的接收者做更改

使用时存在两种隐式转换

指针方法和值方法都可以在指针或非指针上被调用

  1. 值类型的接收者也可以使用指针类型的调用(实参接收者是 *T 类型的变量,而形参接收者是 T 类型,编译器会隐式的解引用接收者,获取实际的取值 )

    a.printName()    // 隐式转换为(*a)
  2. 指针类型的接收者也可以使用值类型的调用(实参接收者是 T 类型的变量,而形参接收者是 *T 类型,编译器会隐式的获取变量的地址)

    f.setName("Fred1")   // 隐式转换为(&f)

示例代码

// 这个 `person` 结构体有两个字段
type Person struct {
    name string
    age  int
}

// 定义普通函数
func printName(name string) {
    fmt.Printf("Name is %s \n", name)
}

// 定义 Person 类型的方法(函数名前面多加了一个参数, 这个 p 是这个方法的接收者)
// 定义值接收者
func (p Person) printName() {
    fmt.Printf("Name is %s \n", p.name)
}

// 定义指针接收者
func (p *Person) setName(name string) {
    p.name = name
}
a := &Person{name: "Ann", age: 40}
a.setName("Ann1")
// 值类型的接收者也可以使用指针类型的调用
a.printName()   // 隐式转换为(*a)
// 实际上编译器,已经对该类型进行了如下转换
(*a).printName()
f := Person{name: "Fred", age: 30}
// 虽然 setName 这个方法要求接收者是一个指针,但我们可以使用简写
// 实际上编译器会对变量进行 &f 的隐式转换(只允许变量)
f.setName("Fred1")   // 隐式转换为(&f)
(&f).setName("Fred2")

f.printName() 

在定义方法接收者时,应该使用指针接收者还是值接收者?

什么时候该使用 pointer receiver

  1. 如果想在方法中改变接收者的状态,操作它的值,使用指针接收器。
  2. 效率:如果实参定义的 struct 很大,避免复制整个实参。
  3. 一致性:习惯上遵循如果 Struct 的任何一个方法使用了指针接收者,那么所有的 struct 方法都应该使用指针接收者,即使有些方法并不一定需要。

什么时候使用 value receiver

  1. 如果不需要编辑接收者的值

参考