Golang中的方法集問(wèn)題

問(wèn)題提要

之前寫(xiě)代碼的時(shí)候遇到了一個(gè)問(wèn)題:自己編寫(xiě)了一個(gè)接口,然后又寫(xiě)了一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)這個(gè)接口,在通過(guò)函數(shù)調(diào)用接口方法時(shí)出現(xiàn)了問(wèn)題。
代碼如下:

type Validator interface  {
  Valid() bool
}

type LoginInput struct {
  Username string
  Password string
}

func (input *LoginInput) Valid() bool {
  // 一些檢驗(yàn)邏輯
  // 返回校驗(yàn)結(jié)果
}

func Handle(v Validator) {
  res := v.Valid()
  // 根據(jù)校驗(yàn)結(jié)果做一些邏輯處理
}

func main() {
  // 對(duì)具體過(guò)程做了提煉,最終邏輯一致
  input := LoginInput{Username: "XXX", Password: "YYY"}
  Handle(input)
}

main中調(diào)用Handle()時(shí)傳參失敗,Goland提示消息如下:Cannot use 'input' (type LoginInput) as type ValidatorType does not implement 'Validator' as 'Valid' method has a pointer receiver

解決方法其實(shí)很簡(jiǎn)單,就是調(diào)用Handle()時(shí)不要傳值,要傳指針。把調(diào)用改成這樣就行:Handle(&input)

但這是為什么呢?回去翻了翻書(shū),發(fā)現(xiàn)是因?yàn)?code>方法集。

什么是方法集

我們先來(lái)看看Golang官方對(duì)它的描述:

https://golang.google.cn/ref/spec#Method_sets
A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. 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). Further rules apply to structs containing embedded fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.
The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.
一個(gè)類型會(huì)有一個(gè)與它關(guān)聯(lián)的方法集。interface類型的方法集就是接口本身。其他任意類型T的方法集由接收者為T類型的全部方法構(gòu)成。對(duì)應(yīng)的指針類型*T的方法集是由接收者為T*T的全部方法構(gòu)成的(也就是說(shuō),它也包含了T的方法集)。更多的規(guī)則應(yīng)用在包含嵌入字段的結(jié)構(gòu)體上,就像struct types章節(jié)中描述的一樣。任何其他類型都有一個(gè)空的方法集。在方法集中,每個(gè)方法必須具有唯一的非空方法名。
類型的方法集確定類型實(shí)現(xiàn)的接口以及可以使用該類型的接收器調(diào)用的方法。

總結(jié)一下官方文檔表述的意思,我們得到如下一張表:

變量類型 方法接收器類型
T (t T)
*T (t T) + (t *T)

對(duì)于T類型,它的方法集只包含接收者類型是T的方法;而對(duì)于*T類型,它的方法集則包含接收者為T*T類型的方法,也就是全部方法。
只有一個(gè)類型的方法集完全涵蓋了接口的方法集后,這個(gè)類型才會(huì)被認(rèn)為是接口的實(shí)現(xiàn)類型。
從這里可以看出來(lái),我們最開(kāi)始的代碼就是因?yàn)?code>LoginInput類型的方法集中沒(méi)有notify方法,所以函數(shù)調(diào)用失敗了。

結(jié)構(gòu)體的方法調(diào)用與方法集之間的關(guān)系

其實(shí)到這里就會(huì)有個(gè)疑問(wèn):平時(shí)調(diào)用方法時(shí),無(wú)論變量類型是值類型還是指針類型都能調(diào)用成功,也沒(méi)出過(guò)問(wèn)題啊。
這里其實(shí)是Golang的一個(gè)語(yǔ)法糖:在使用選擇器(Selectors)調(diào)用方法時(shí),編譯器會(huì)幫你做好取址或取值的操作的。
下面通過(guò)代碼說(shuō)明一下這個(gè)關(guān)系:

type StructA struct {}

func (s StructA) ValueReceiver () {}

func (s *StructA) PointReceiver () {}

func main() {
  value := StructA{}
  point := &value
  // 編譯器不做處理,就是value.ValueReceiver()
  value.ValueReceiver()
  // 其實(shí)是(&value).ValueReceiver()的簡(jiǎn)便寫(xiě)法
  value.PointReceiver()
  // 其實(shí)是(*point).ValueReceiver()的簡(jiǎn)便寫(xiě)法
  point.ValueReceiver()
  // 編譯器不做處理,就是point.ValueReceiver()
  point.PointReceiver()
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容