在 方法基础 一节中,我们已经学习了Go方法的基础知识,今天这节我们主要围绕基于指针对象的方法来深入讨论总结一下。
基于指针对象的方法
我们知道,Go语言中参数传递都是值传递。当我们需要更新一个变量,或者参数较大时,我们希望能够避免这样的拷贝,这个时候我们就需要用到指针了。所以,对应的 我们用指针来作为接收器,就达到了同样的目的。
//方法名字是(*Point).ScaleBy,注意这里的括号是必须的
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
//常规调用
r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"
//简短调用
//编译器会隐式地帮我们用&p去调用ScaleBy这个方法
p := Point{1, 2}
p.ScaleBy(2)
值接收者声明的方法,调用时会使用这个值的一个副本去执行,而指针接收者在调用者会共享调用方法时接收者所指向的值,即可以修改指向的值。
一般来讲,如果某一个类(比如上面示例程序中的Point)中有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数。 另外,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的。
type P *int
func (P) f() { /* ... */ } // compile error: invalid receiver type
关于拷贝相关的,有一个特别需要注意的地方:
- 如果命名类型T的所有方法都是用T类型自己来做接收器(而不是*T),那么拷贝这种类型的实例就是安全的;调用他的任何一个方法也就会产生一个值的拷贝。
- 如果命名类型T的一个方法使用指针作为接收器,那么拷贝这种类型的实例就是不安全的;你需要避免对其进行拷贝,因为这样可能会破坏掉该类型内部的不变性。对拷贝后的变量进行修改可能会有让你有意外的结果。
Nil是一个合法的接收器类型
方法理论上可以用nil指针作为其接收器,尤其当nil对于对象来说是合法的零值时,比如map或者slice。在下面的简单int链表的例子里,nil代表的是空链表(注意这个时候对于nil代表的含义有一个明确的注释变得非常重要):
// An IntList is a linked list of integers.
// A nil *IntList represents the empty list.
type IntList struct {
Value int
Tail *IntList
}
// Sum returns the sum of the list elements.
func (list *IntList) Sum() int {
if list == nil {
return 0
}
return list.Value + list.Tail.Sum()
}
指针类型接收者方法就总结到这里,你Get了吗?