开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
本节简要介绍 Go 语言中的方法与接口,虽然与其它面向对象对象的语言有所差异,但基本上可以找到类似对应的概念。
- Go没有类,但是可以为类型定义方法。
- 方法是拥有接收者参数的函数。
- 可以为非结构体类型声明方法。但是方法的接收者必须是在同一个包内定义的类型。
- 使用指针接收者的方法可以修改接收者指向的值,使用值接收者的方法只能对原值的拷贝进行操作。所以使用指针接受者的做法更常见。
- 使用指针参数的函数只能接收对应类型的指针,而使用值参数的函数只能接收对应类型的值;相比之下,使用指针/值接收者的方法在调用时,将同时允许对应类型的值或指针为接收者。也就是说,Go可以根据方法的接收者类型将
v.Method()解释为(&v).Method(),或者将p.Method()解释为(*p).Method()。 - 使用指针接收者的理由:可以修改接收者指向的值;避免值的复制。通常来讲,给定类型的方法不要混用这两种做法。
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10) // 解释为 (&v).Scale
fmt.Println(v.Abs())
}
- 一个接口类型定义为一组方法签名的集合。一个接口值可以保存任何实现了该接口的类型的值。
- 一个类型通过实现一个接口的所有方法来隐式实现该接口,此外并没有显式的声明或者类似"implements"这样的关键字。
- 一个接口值保存了一个具体类型的值
(value, type),调用接口值的方法会执行具体类型的同名函数。
type I interface {
M()
}
// 类型 *T 隐式实现了接口 I。注意,不是类型 T
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
// 类型 F 隐式实现了接口 I
type F float64
func (f F) M() {
fmt.Println(f)
}
func main() {
var i I
i = &T{"Hello"}
i.M()
i = F(math.Pi)
i.M()
}
-
如果一个接口的具体值为 nil,调用其方法将使用一个 nil 的接收者。因此方法中需要处理接收者为 nil 的情况。注意,具体值为 nil 的接口值本身不为 nil。
-
一个 nil 的接口值不保存值或者类型
var i I,调用其方法会产生运行时错误。 -
未指定任何方法的接口类型是空接口。空接口可以保存任何类型的值,它通常被用来处理未知类型的值。
-
类型断言提供了访问接口具体值的方法。语句
t := i.(T)断言接口值i保存的具体类型为T,并将该T类型的值赋给变量t。a. 如果
i并未保存T类型的值,这将触发panic;b. 类型断言可以返回两个值
t, ok := i.(T),如果断言正确,ok为true,t保存具体值;如果断言错误,ok为false,t为T类型的零值。 -
类型switch与一般的switch语句类似,不过比较的case是类型而非值。
a. 在每一个case内,变量
v将以对应的类型保存i的具体值。b. 在
defaultcase中,变量v的类型与i一致。
switch v := i.(type) {
case T:
// here v has type T
case S:
// here v has type S
default:
// no match; here v has the same type as i
}
Stringer是fmt包中定义的一个常见接口,一个Stringer可以用一个字符串描述自身。接口包含一个String() string方法。error也是一个内建的接口,包含一个Error() string方法。
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %v", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
z := 1.0
for i := 1; i < 10; i++ {
z -= (z*z - x) / (2*z)
}
return z, nil
}
func main() {
x, err := Sqrt(-2)
if err == nil {
fmt.Println(x)
}
fmt.Println(err)
}
io.Reader是io包中定义的接口,表示数据流的读取端。接口包含一个func (T) Read(b []byte) (n int, err error)方法。