长安链 DApp 开发必学 Go 08

126 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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,这一章主要介绍了“指针接收者”,它能帮助我们修改数据的值,下一章见~