# 11.6 使用方法集與接口
在第 10.6.3 節及例子 methodset1.go 中我們看到,作用於變量上的方法實際上是不區分變量到底是指針還是值的。當碰到接口類型值時,這會變得有點複雜,原因是接口變量中存儲的具體值是不可尋址的,幸運的是,如果使用不當編譯器會給出錯誤。考慮下面的程序:
示例 11.5 methodset2.go:
package main
import (
"fmt"
)
type List []int
func (l List) Len() int {
return len(l)
}
func (l *List) Append(val int) {
*l = append(*l, val)
}
type Appender interface {
Append(int)
}
func CountInto(a Appender, start, end int) {
for i := start; i <= end; i++ {
a.Append(i)
}
}
type Lener interface {
Len() int
}
func LongEnough(l Lener) bool {
return l.Len()*10 > 42
}
func main() {
// A bare value
var lst List
// compiler error:
// cannot use lst (type List) as type Appender in argument to CountInto:
// List does not implement Appender (Append method has pointer receiver)
// CountInto(lst, 1, 10)
if LongEnough(lst) { // VALID:Identical receiver type
fmt.Printf("- lst is long enough\n")
}
// A pointer value
plst := new(List)
CountInto(plst, 1, 10) //VALID:Identical receiver type
if LongEnough(plst) {
// VALID: a *List can be dereferenced for the receiver
fmt.Printf("- plst is long enough\n")
}
}
討論
在 lst
上調用 CountInto
時會導致一個編譯器錯誤,因爲 CountInto
需要一個 Appender
,而它的方法 Append
只定義在指針上。 在 lst
上調用 LongEnough
是可以的因爲 'Len' 定義在值上。
在 plst
上調用 CountInto
是可以的,因爲 CountInto
需要一個 Appender
,並且它的方法 Append
定義在指針上。 在 plst
上調用 LongEnough
也是可以的,因爲指針會被自動解引用。
總結
在接口上調用方法時,必須有和方法定義時相同的接收者類型或者是可以從具體類型 P
直接可以辨識的:
- 指針方法可以通過指針調用
- 值方法可以通過值調用
- 接收者是值的方法可以通過指針調用,因爲指針會首先被解引用
- 接收者是指針的方法不可以通過值調用,因爲存儲在接口中的值沒有地址
將一個值賦值給一個接口賦值時,編譯器會確保所有可能的接口方法都可以在此值上被調用,因此不正確的賦值在編譯期就會失敗。
譯註
Go 語言規範定義了接口方法集的調用規則:
- 類型 T 的可調用方法集包含接受者爲 T 或 T 的所有方法集
- 類型 T 的可調用方法集包含接受者爲 T 的所有方法
- 類型 T 的可調用方法集不包含接受者爲 *T 的方法
鏈接
- 目錄
- 上一節:測試一個值是否實現了某個接口
- 下一節:第一個例子:使用 Sorter 接口排序