开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
本文已参与「开源摘星计划」,欢迎正在阅读的你加入。活动链接:github.com/weopenproje…
经过上面几章的学习,我们已经掌握了 go 语言中“方法”(Method)的使用方式。详细的介绍了“接收者”(Receiver),它分为“指针接收者”(Pointer Receiver)和“数值接收者”(Value Reciever)。
根据官方教程的建议,我们大部分的时候,都要使用“指针接收者”,不管是否需要修改结构体的属性,这也符合在其他语言中使用“类”时的逻辑。
这一章,继续介绍 go 语言中一种新的类型——“接口”(Interface)类型。
接口类型内定义了一系列方法的签名。当我们定义结构体,并实现了相应的方法时,就算实现了接口。定义都是比较抽象的,让我们来看代码:
package main
import (
"fmt"
"math"
)
type Abser interface { // 定义接口 Abser
Abs() float64 // 定义接口方法 Abs
}
func main() {
var a Abser // a 为自定义的接口类型——Abser
// MyFloat 和 Vertex 都在下方实现了 Abs 方法
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}
a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser
注意!当使用了指针接收者时,必须获取 v 的指针,而不是 v 本身。下面的写法会报错
// a = v
fmt.Println(a.Abs())
}
type MyFloat float64 // 定义 MyFloat 并实现接口的方法
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
type Vertex struct { // 定义 Vertex 并实现接口的方法
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
可以看到,go 语言中的“接口”类似于其他语言中的“抽象函数”。通过“接口”,定义一系列方法的签名,然后在具体的“方法”中实现。
根据官方的教程,go 语言中的接口实现是“隐式的”(implicitly),即咱们只要实现了接口里定义好的方法签名,go 会自动的帮我们进行映射:
package main
import "fmt"
type I interface { // 定义接口 I,包含方法 M 的签名
M()
}
type T struct { // 定义结构体 T
S string
}
func (t T) M() { // 实现结构体 T 的 M 方法
fmt.Println(t.S)
}
func main() {
var i I = T{"hello"} // 定义一个 I 类型的 i 变量
i.M()
}
结构体 T 实现了 M 方法,即成为接口类型 I。根据官方教程的说法,这样做的好处是可以“解耦”接口的定义和实现。
作为 python 的拥趸,我个人是不赞成这样的做法。能写清楚的,尽量写清楚。而且,这样也会使得“命名空间”互相侵入,无法施展威力。
python 如果能变成 typepython,我想一定会更加流行~
ok,下一章我们继续探索 go 的接口(interface)