5.2 方法
方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字 func
和方法名之间增加了一个参数,如代码清单 5-11 所示。
代码清单 5-11 listing11.go
01 // 这个示例程序展示如何声明
02 // 并使用方法
03 package main
04
05 import (
06 "fmt"
07 )
08
09 // user 在程序里定义一个用户类型
10 type user struct {
11 name string
12 email string
13 }
14
15 // notify 使用值接收者实现了一个方法
16 func (u user) notify() {
17 fmt.Printf("Sending User Email To %s<%s>\n",
18 u.name,
19 u.email)
20 }
21
22 // changeEmail 使用指针接收者实现了一个方法
23 func (u *user) changeEmail(email string) {
24 u.email = email
25 }
26
27 // main 是应用程序的入口
28 func main() {
29 // user 类型的值可以用来调用
30 // 使用值接收者声明的方法
31 bill := user{"Bill", "bill@email.com"}
32 bill.notify()
33
34 // 指向 user 类型值的指针也可以用来调用
35 // 使用值接收者声明的方法
36 lisa := &user{"Lisa", "lisa@email.com"}
37 lisa.notify()
38
39 // user 类型的值可以用来调用
40 // 使用指针接收者声明的方法
41 bill.changeEmail("bill@newdomain.com")
42 bill.notify()
43
44 // 指向 user 类型值的指针可以用来调用
45 // 使用指针接收者声明的方法
46 lisa.changeEmail("lisa@newdomain.com")
47 lisa.notify()
48 }
代码清单 5-11 的第 16 行和第 23 行展示了两种类型的方法。关键字 func
和函数名之间的参数被称作 接收者 ,将函数与接收者的类型绑在一起。如果一个函数有接收者,这个函数就被称为 方法 。当运行这段程序时,会得到代码清单 5-12 所示的输出。
代码清单 5-12 listing11.go 的输出
Sending User Email To Bill<bill@email.com>
Sending User Email To Lisa<lisa@email.com>
Sending User Email To Bill<bill@newdomain.com>
Sending User Email To Lisa<lisa@comcast.com>
让我们来解释一下代码清单 5-13 所示的程序都做了什么。在第 10 行,该程序声明了名为 user
的结构类型,并声明了名为 notify
的方法。
代码清单 5-13 listing11.go:第 09 行到第 20 行
09 // user 在程序里定义一个用户类型
10 type user struct {
11 name string
12 email string
13 }
14
15 // notify 使用值接收者实现了一个方法
16 func (u user) notify() {
17 fmt.Printf("Sending User Email To %s<%s>\n",
18 u.name,
19 u.email)
20 }
Go 语言里有两种类型的接收者: 值接收者 和 指针接收者 。在代码清单 5-13 的第 16 行,使用值接收者声明了 notify
方法,如代码清单 5-14 所示。
代码清单 5-14 使用值接收者声明一个方法
func (u user) notify() {
notify
方法的接收者被声明为 user
类型的值。如果使用值接收者声明方法,调用时会使用这个值的一个副本来执行。让我们跳到代码清单 5-11 的第 32 行来看一下如何调用 notify
方法,如代码清单 5-15 所示。
代码清单 5-15 listing11.go:第 29 行到第 32 行
29 // user 类型的值可以用来调用
30 // 使用值接收者声明的方法
31 bill := user{"Bill", "bill@email.com"}
32 bill.notify()
代码清单 5-15 展示了如何使用 user
类型的值来调用方法。第 31 行声明了一个 user
类型的变量 bill
,并使用给定的名字和电子邮件地址做初始化。之后在第 32 行,使用变量 bill
来调用 notify
方法,如代码清单 5-16 所示。
代码清单 5-16 使用变量来调用方法
bill.notify()
这个语法与调用一个包里的函数看起来很类似。但在这个例子里, bill
不是包名,而是变量名。这段程序在调用 notify
方法时,使用 bill
的值作为接收者进行调用,方法 notify
会接收到 bill
的值的一个副本。
也可以使用指针来调用使用值接收者声明的方法,如代码清单 5-17 所示。
代码清单 5-17 listing11.go:第 34 行到第 37 行
34 // 指向 user 类型值的指针也可以用来调用
35 // 使用值接收者声明的方法
36 lisa := &user{"Lisa", "lisa@email.com"}
37 lisa.notify()
代码清单 5-17 展示了如何使用指向 user
类型值的指针来调用 notify
方法。在第 36 行,声明了一个名为 lisa
的指针变量,并使用给定的名字和电子邮件地址做初始化。之后在第 37 行,使用这个指针变量来调用 notify
方法。为了支持这种方法调用,Go 语言调整了指针的值,来符合方法接收者的定义。可以认为 Go 语言执行了代码清单 5-18 所示的操作。
代码清单 5-18 Go 在代码背后的执行动作
(*lisa).notify()
代码清单 5-18 展示了 Go 编译器为了支持这种方法调用背后做的事情。指针被解引用为值,这样就符合了值接收者的要求。再强调一次, notify
操作的是一个副本,只不过这次操作的是从 lisa
指针指向的值的副本。
也可以使用指针接收者声明方法,如代码清单 5-19 所示。
代码清单 5-19 listing11.go:第 22 行到第 25 行
22 // changeEmail 使用指针接收者实现了一个方法
23 func (u *user) changeEmail(email string) {
24 u.email = email
25 }
代码清单 5-19 展示了 changeEmail
方法的声明。这个方法使用指针接收者声明。这个接收者的类型是指向 user
类型值的指针,而不是 user
类型的值。当调用使用指针接收者声明的方法时,这个方法会共享调用方法时接收者所指向的值,如代码清单 5-20 所示。
代码清单 5-20 listing11.go:第 36 行和第 44 行到第 46 行
36 lisa := &user{"Lisa", "lisa@email.com"}
44 // 指向 user 类型值的指针可以用来调用
45 // 使用指针接收者声明的方法
46 lisa.changeEmail("lisa@newdomain.com")
在代码清单 5-20 中,可以看到声明了 lisa
指针变量,还有第 46 行使用这个变量调用了 changeEmail
方法。一旦 changeEmail
调用返回,这个调用对值做的修改也会反映在 lisa
指针所指向的值上。这是因为 changeEmail
方法使用了指针接收者。总结一下,值接收者使用值的副本来调用方法,而指针接受者使用实际值来调用方法。
也可以使用一个值来调用使用指针接收者声明的方法,如代码清单 5-21 所示。
代码清单 5-21 listing11.go:第 31 行和第 39 行到第 41 行
31 bill := user{"Bill", "bill@email.com"}
39 // user 类型的值可以用来调用
40 // 使用指针接收者声明的方法
41 bill.changeEmail("bill@newdomain.com")
在代码清单 5-21 中可以看到声明的变量 bill
,以及之后使用这个变量调用使用指针接收者声明的 changeEmail
方法。Go 语言再一次对值做了调整,使之符合函数的接收者,进行调用,如代码清单 5-22 所示。
代码清单 5-22 Go 在代码背后的执行动作
(&bill).changeEmail("bill@newdomain.com")
代码清单 5-22 展示了 Go 编译器为了支持这种方法调用在背后做的事情。在这个例子里,首先引用 bill
值得到一个指针,这样这个指针就能够匹配方法的接收者类型,再进行调用。Go 语言既允许使用值,也允许使用指针来调用方法,不必严格符合接收者的类型。这个支持非常方便开发者编写程序。
应该使用值接收者,还是应该使用指针接收者,这个问题有时会比较迷惑人。可以遵从标准库里一些基本的指导方针来做决定。后面会进一步介绍这些指导方针。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论