返回介绍

5.2 方法

发布于 2024-10-11 12:39:00 字数 5242 浏览 0 评论 0 收藏 0

方法能给用户定义的类型添加新的行为。方法实际上也是函数,只是在声明时,在关键字 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文