跳到主要内容

方法与接口

方法

声明方法

与函数的区别是多了一个 receiver 参数,receiver 参数是方法的接收者,可以是值或者指针,方法的 receiver 参数必须位于方法名之前。

func (variable type) MethodName(parameters ...) {
// method functionality
}

虽然是叫receiver,我个人的理解是-谁可以调用这个方法/函数

type triangle struct {
size int
}

func (t triangle) perimeter() int {
return t.size * 3
}
func main() {
t := triangle{3}
fmt.Println("Perimeter:", t.perimeter())
}

方法中的指针

有时,方法需要更新变量。 或者,如果方法的参数太大,你可能希望避免复制它。 在遇到此类情况时,你需要使用指针传递变量的地址。 在之前的模块中,当我们在讨论指针时提到,每次在 Go 中调用函数时,Go 都会复制每个参数值以便使用。

如果你需要更新方法中的接收方变量,也会执行相同的行为。 例如,假设你要创建一个新方法以使三角形的大小增加一倍。 你需要在接收方变量中使用指针,具体如下所示:

func (t *triangle) doubleSize() {
t.size *= 2
}

声明其他类型的方法

方法的一个关键方面在于,需要为任何类型定义方法,而不只是针对自定义类型(如结构)进行定义。 但是,你不能通过属于其他包的类型来定义结构。 因此,不能在基本类型(如 string)上创建方法。

尽管如此,你仍然可以利用一点技巧,基于基本类型创建自定义类型,然后将其用作基本类型。 例如,假设你要创建一个方法,以将字符串从小写字母转换为大写字母。 你可以按如下所示写入方法:

package main

import (
"fmt"
"strings"
)

type upperstring string

func (s upperstring) Upper() string {
return strings.ToUpper(string(s))
}

func main() {
s := upperstring("Learning Go!")
fmt.Println(s)
fmt.Println(s.Upper())
}

嵌入方法

例如,假设你想要创建一个带有逻辑的新三角形结构,以加入颜色。 此外,你还希望继续使用之前声明的三角形结构。 因此,彩色三角形结构将如下所示:

type coloredTriangle struct {
triangle
color string
}
func main() {
t := coloredTriangle{triangle{3}, "blue"}
fmt.Println("Size:", t.size)
fmt.Println("Perimeter", t.perimeter())
}

如果你熟悉 Java 或 C++ 等 OOP 语言,则可能会认为 triangle 结构看起来像基类,而 coloredTriangle 是一个子类(如继承),但事实并不是如此。 实际上,Go 编译器会通过创建如下的包装器方法来推广 perimeter() 方法:

func (t coloredTriangle) perimeter() int {
return t.triangle.perimeter()
}

重载方法

如果彩色三角形的周长是普通三角形的两倍,则代码将如下所示:

func (t coloredTriangle) perimeter() int {
return t.size * 3 * 2
}

如果你仍需要从 triangle 结构调用 perimeter() 方法,则可通过对其进行显示访问来执行此操作,如下所示:

func main() {
t := coloredTriangle{triangle{3}, "blue"}
fmt.Println("Size:", t.size)
fmt.Println("Perimeter (colored)", t.perimeter())
fmt.Println("Perimeter (normal)", t.triangle.perimeter())
}

输出:

Size: 3
Perimeter (colored) 18
Perimeter (normal) 9

方法中的封装

“封装”表示对象的发送方(客户端)无法访问某个方法。 通常,在其他编程语言中,你会将 private 或 public 关键字放在方法名称之前。 在 Go 中,只需使用大写标识符,即可公开方法,使用非大写的标识符将方法设为私有方法。

Go 中的封装仅在程序包之间有效。 换句话说,你只能隐藏来自其他程序包的实现详细信息,而不能隐藏程序包本身。

如要进行尝试,请创建新程序包 geometry 并按如下方式将三角形结构移入其中:

package geometry

type Triangle struct {
size int
}

func (t *Triangle) doubleSize() {
t.size *= 2
}

func (t *Triangle) SetSize(size int) {
t.size = size
}

func (t *Triangle) Perimeter() int {
t.doubleSize()
return t.size * 3
}
func main() {
t := geometry.Triangle{}
t.SetSize(3)
fmt.Println("Perimeter", t.Perimeter())
}