method set
, interface
, auto deference or reference
當這些語法綜合再一起使用時,很容易混淆,甚至寫出 bug code,這章我們就透過題目探討一下 method set
常常被大家誤會的地方。
Golang 78 語法篇 - interface % Line 50 用到的概念就在這篇。
題目
請問以下程式輸出結果為何
type Employee struct {}
func (e Employee) DoWork() {
fmt.Println("do work")
}
func main() {
(&Employee{}).DoWork()
}
答案只有兩種方向: 1. 無法編譯 2. 印出 do work
這個程式可以編譯,並且還印出 do work 但是為什麼呢?
Method set
在 receiver argument 和 receiver parameter 不一樣的情況下,直覺反應就是 implicit dereference
和 implicit reference
在背後會幫忙,但是這個寫法 (struct literal) 是不會有此情況的。
既然沒有 implicit dereference
和 implicit reference
的支援,那一定存在其它機制使得這段 code 能正常編譯。
以下從 Golang Specification所截取的句子:
The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T)
以下是從 Golang FAQ 所截取的句子:
As the Go specification says, the method set of a type T consists of all methods with receiver type T, while that of the corresponding pointer type *T consists of all methods with receiver *T or T. That means the method set of *T includes that of T, but not the reverse
其實意思就是型態 T 的所有 method 包含了所有 receiver 型態是 T 的 method,這理所當然,看起來也像廢話,另一句才是關鍵,也是今日的重點:*T 型態的所有 method 包含了 receiver 型態是 T 及 *T 的 method。
有了此結論我們就能知道 *Employee
的 method set 裡是有 DoWork()
,所以編譯會過。
那這就有趣了,再看一個延伸題,請問這段 code 編譯會不會過:
type Worker interface {
DoWork()
}
type Employee struct {
}
func (e Employee) DoWork() {
fmt.Println("do work")
}
func main() {
var w Worker = &Employee{}
}
這段 code 問的其實就是 Employee 到底有沒有符合(satisfy) Worker
介面,根據剛剛所得出的結論, *Employee
的 method set 裡是有包含 DoWork
的,所以編譯是會過的。