Go语言中,方法method是特定于某一类型的函数。类似于其他语言中的self或this,Go中的方法函数也有着一个特殊的参数--接收者。Go方法中的接收者可分为值类型接收者和指针类型接收者,两者在方法调用方面由于Golang语法糖的原因,几乎没有区别。两者之间的区别主要在参数传递和方法实现两方面。
- 参数传递
同普通函数传参一样,值类型接收者和指针类型接收者作为方法参数时都会进行副本拷贝,区别在于拷贝的对象。对于值类型的接收者拷贝的是对应类型值本身,而对于指针类型接收者,拷贝的是对应类型的指针。这就造成了在方法内对值类型接收者进行修改时,如果对应类型为
非引用类型,则修改对象仅为数据副本,不会影响到原始数据。实例代码如下:
type Student struct {
Age int
}
// 指针接收者实现AgeIncr
func (s *Student) AgeIncr() {
s.Age++
}
type Teacher struct {
Age int
}
// 值接收者实现AgeIncr
func (t Teacher) AgeIncr() {
t.Age++
}
func main() {
s1 := &Student{
Age: 15,
}
fmt.Println("before s1:", s1) // before s1: &{15}
s1.AgeIncr()
fmt.Println("after s1:", s1) // after s1: &{16}
t1 := &Teacher{
Age: 20,
}
fmt.Println("before t1:", t1) // before t1: &{20}
t1.AgeIncr()
fmt.Println("after t1:", t1) // after t1: &{20}
}
此处并不是说值接收者就无法修改到对应类型值本身了,对于引用类型依旧可以。验证代码如下:
type ValRec1 map[int]int
type ValRec2 []int
func (v ValRec1) Modify(x int) {
v[x] = x
}
func (v ValRec2) Modify(x int) {
for idx := range v {
v[idx] = x
}
}
func main() {
vr1 := make(ValRec1)
fmt.Println("before vr1:", vr1) // before vr1: map[]
vr1.Modify(3)
fmt.Println("after vr1:", vr1) // after vr1: map[3:3]
vr2 := make(ValRec2, 2)
fmt.Println("before vr2:", vr2) // before vr2: [0 0]
vr2.Modify(3)
fmt.Println("after vr2:", vr2) // after vr2: [3 3]
}
- 方法实现 使用值类型接收者实现方法时,编译器会自动实现对应的指针类型接收者的方法。而使用指针类型接收者实现方法时,编译器不会追加值类型接收者的方法实现。验证代码如下:
type Runner interface {
Run()
}
type Bike struct{}
func (b Bike) Run() {
fmt.Println("bike run")
}
type Car struct{}
func (c *Car) Run() {
fmt.Println("car run")
}
func main() {
var runner Runner
b := Bike{}
runner = b
runner.Run()
pb := &Bike{}
runner = pb
runner.Run()
// 报错如下
// cannot use c (variable of type Car) as type Runner in assignment:
// Car does not implement Runner (Run method has pointer receiver)
// c := Car{}
// runner = c
// runner.Run()
pc := &Car{}
runner = pc
runner.Run()
}