hello云胜

技术与生活

0%

Go中的方法

Go 语言从设计之初,就不支持经典的面向对象语法元素,比如类、对象、继 承,等等,但 Go 语言仍保留了名为“方法(method)”的语法元素。当然,Go 语言中 的方法和面向对象中的方法并不是一样的

方法的定义

go中方法的定义和函数的定义很像

1
2
3
4
5
6
7
8
type People struct {
name string
age int
}

func (p People) String() string {
return fmt.Sprintf("people : %s, %s", p.name, strconv.Itoa(p.age))
}
1
2
3
4
func (t *T或T) MethodName(参数列表) (返回值列表) {
// 方法体
}

和函数最大的不同就是这个receiver部分。这个receiver部分是一个类型。作用是指明这个方法的归属。就可以说这个类型有这样一个方法。

每个方法只能有一个 receiver 参数

receiver 参数的基类型本身不能为指针类型或接口类型

Go 要求,方法声明要与 receiver 参数的基 类型声明放在同一个包内。也就是说我们不能给go的原生类型(比如int,string)等添加方法,也不能给其他包的类型添加方法。

方法的本质是函数

java的方法在调用时,实际上编译器会自动将this作为第一参数传入方法中。go的原理也类似。

所以上面的

1
2
3
func (p People) String() string {
return fmt.Sprintf("people : %s, %s", p.name, strconv.Itoa(p.age))
}

可以转换为函数

1
2
3
func String(p People) string {
return fmt.Sprintf("people : %s, %s", p.name, strconv.Itoa(p.age))
}

这种等价转换后的函数的类型就是方法的类型

所以,Go 语言中的方法的本质就是,一个以方法的 receiver 参数 作为第一个参数的普通函数。

receiver的类型选择问题

首先看这个例子

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

type Employee struct {
name string
company string
}

// 值方法
// 值方法的接收者是该方法所属的那个类型值的一个副本。我们在该方法内对该副本的修改不会体现在原值上
func (e Employee) SetName(name string) {
e.name = name
}

// 指针方法
func (e *Employee) setCompany(company string) {
e.company = company
}

func main() {
e := Employee{
name: "alex",
company: "google",
}
fmt.Printf("=======:%s\n", e)

e.SetName("zhangsan")
fmt.Printf("=======:%s\n", e)

e.setCompany("baidu")
fmt.Printf("=======:%s\n", e)

}

结果

1
2
3
=======:{alex google}
=======:{alex google}
=======:{alex baidu}

两个set方法,一个的receiver是类型,一个是类型指针。

之前就说过Go 函数的参数采用的是值拷贝传递,SetName中对receiver的修改,只会影响e的副本,不会影响e本身。

类型是*Employee的setCompany方法,因为传递的是地址,所以会影响。

所以,选择receiver类型的原则:

  1. 如果需要将修改反应在原实例上,用*T

    无论是 T 类型实例,还是 *T 类型实例,都既 可以调用 receiver 为 T 类型的方法,也可以调用 receiver 为 *T 类型的方法。这是go的语法糖自动转换

  2. 还是因为值拷贝问题,用T作为receiver类型,如果实例很大,会有较大的性能开销,这种情况下还是用*T比较好

  3. 根据T类型是否需要实现某个接口。是,则需要使用T类型作为receiver。

    另外Go 语言规定,*T 类型的方法集合包含所有以 *T 为 receiver 参数类型的方 法,以及所有以 T 为 receiver 参数类型的方法。