Go方法

方法

Go 没有类,所以也就不存在真正意义上的方法,Go 中定义的方法是指定了接收者的一种函数
方法只是个带接收者参数的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 自定义一个结构体
type Vertex struct {
X, Y float64
}
// 这也是自定义结构
type MyFloat float64

// 这是方法
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 这是函数
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// 使用方法
func main() {
v := Vertex{3, 4}
// 很像对象调用方法,但其实不是
fmt.Println(v.Abs())
}

方法和函数的区别就是在 func 关键字后面有没有跟接收者参数
而且只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括 int 之类的内建类型)的接收者声明方法

所以对于方法的定义我是这么理解的:
在一个包里面,可以自定义结构类型(比如结构体),这些自定义结构所定义的变量就是方法的”目标客户”
这些变量就可以使用那些以他们为”目标客户”的方法,别的自定义结构的变量就不行

方法和指针

众所周知,带指针参数的函数必须接受一个指针

1
2
3
4
5
6
func Scale(v *Vertex, f float64) {
// 函数
}
var v Vertex
Scale(v, 5) // 编译错误!
Scale(&v, 5) // OK

但是如果是以指针为接收者的方法的话,接收者既能为值又能为指针

1
2
3
4
5
6
7
func (v *Vertex) Scale(f float64) {
// 方法
}
var v Vertex
v.Scale(5) // OK
p := &v
p.Scale(10) // OK

使用指针为接收者的方法是有好处的:方法会直接作用于这个接收者,避免了每次调用方法都要复制一次该值
但是值作为接收者和指针作为接收者不能混用

原因:
接收者是值类型的方法,会自动实现接收者是指针类型的方法
而接收者是指针类型的方法,不会自动实现接收者是值类型的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import "fmt"

// 定义了一个接口,含有两个方法
type coder interface {
one()
two()
}

// 定义一个结构体
type Vertex struct {
S string
}

// 接收者为值类型的方法
func (v Vertex) one() {
fmt.Printf("I am one\n")
}

// 接收者为指针类型的方法
func (v *Vertex) two() {
fmt.Printf("I am two\n")
}

func main() {
var c coder = &Vertex{"Go"}
c.one()
c.two()

// 报错
// var c coder = Vertex{"Go"}
// c.one()
// c.two()
}