开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情
本文已参与「开源摘星计划」,欢迎正在阅读的你加入。活动链接:github.com/weopenproje…
上一章,介绍了 go 的“方法”(Methods)。通过给函数绑定“接收者”(receiver)——可以是结构体或者其他类型的数据,就可以实现自定义逻辑的“方法”,从而实现“类”的功能。
在上一章的代码示例中,在给结构体定义的方法中,我们调用了结构体中定义的属性。但是,如果想要修改结构体的属性,在 go 中要怎么实现呢?
让我们看下示例代码:
package main
import (
"fmt"
"math"
)
type Vertex struct { // 定义结构体 Vertex,它有两个属性 X 和 Y
X, Y float64
}
func (v Vertex) Abs() float64 { // 给 Vertex 定义方法 Abs
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) { // 给 Vertex 定义方法 Scale,注意结构体前加上了星号——*
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4} // 初始化 Vertex
v.Scale(10) // 调用 Scale 方法修改 v 的属性值
fmt.Println(v.Abs()) // 调用 Abs 方法
}
跟上一章的例子类似,不同的是,我们定义了一个“指针接收者”(Pointer receivers),也就是在绑定方法的时候,给结构体前面加上星号—— * 。这样,在 go 运行时,就会将初始化的数据传入,从而可以修改它的值。不然,go 只会将数据的备份传入,所以咱们只能访问,而无法修改。(可以试着把 Scale 方法的接收者前的星号去掉,看看代码的运行结果)。
让我们来看一个例子,更好的理解“指针接收者”(Pointer receivers):
package main
import "fmt"
type Vertex struct { // 定义结构体 Vertex
X, Y float64
}
func (v *Vertex) Scale(f float64) { // 定义方法 Scale,此时 v 是个“指针接收者”
v.X = v.X * f
v.Y = v.Y * f
}
func ScaleFunc(v *Vertex, f float64) { // 定义函数 ScaleFunc,它接收 Vertex 指针为参数
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4} // 初始化
v.Scale(2) // 调用方法 Scale
ScaleFunc(&v, 10) // 调用函数 ScaleFunc,注意此时传入的为 v 的指针
p := &Vertex{4, 3} // 初始化,并获取指针
p.Scale(3) // 调用方法 Scale
ScaleFunc(p, 8) // 调用函数 ScaleFunc,此时直接传入指针 P
fmt.Println(v, p) // 打印对比结果
}
运行代码可以发现,这两种调用方式是完全等效的。也就是说,go 在运行时,自动的把 v.Scale(2) 当作了 (&v).Scale(2),即获取v的指针,并调用方法。
同样的,当我们定义的是“数值接收者”(Value receivers),go 运行时也会自动将指针转为具体的数据。
ok,这一章主要介绍了“指针接收者”,它能帮助我们修改数据的值,下一章见~