Golang中的接口與鴨子類型

1 接口的定義與理解

    接口是一個自定義類型,它是一組方法的集合。從定義上來看,接口有兩個特點。第一,接口本質是一種自定義類型,因此不要將golang中的接口簡單理解爲C++/Java中的接口,後者僅用於聲明方法簽名。第二,接口是一種特殊的自定義類型,其中沒有數據成員,只有方法(也可以爲空)。

    接口是完全抽象的,因此不能將其實例化。然而,可以創建一個其類型爲接口的變量,它可以被賦值爲任何滿足該接口類型的實際類型的值。接口的重要特性是:

   (1)只要某個類型實現了接口要的方法,那麼我們就說該類型實現了此接口。該類型的值可以賦給該接口的變量;

   (2)作爲1的推論,任何類型的值都可以賦值給空接口interface{}

    注意:這只是golang中接口的特性,爲非所有類型的特性(接口是一種特殊的類型)。

    接口的特性是golang支持鴨子類型的基礎,即“如果它走起來像鴨子,叫起來像鴨子(實現了接口要的方法),它就是一隻鴨子(可以被賦值給接口的值)”。憑藉接口機制和鴨子類型,golang提供了一種遊離於類、繼承、模板之外的更加靈活強大的選擇。


2 例子

type Exchanger interface {
       exchange()
}
 
type StringPair struct {
       first, second string
}
 
type Point[2]int
 
func (sp *StringPair) exchange() {
       sp.first, sp.second = sp.second, sp.first
}
 
func (p *Point) exchange() {
       p[0], p[1] = p[1], p[0]
}
 
func exchangeThese(exchangers ...Exchanger) {
       for _, exchanger := range exchangers {
              exchanger.exchange()
       }
}
 
func main() {
       pair1 := StringPair{"abc","def"}
       pair2 := StringPair{"ghi","jkl"}
       point := Point{5, 7}
 
       fmt.Println(pair1, pair2, point)
       pair1.exchange()
       pair2.exchange()
       point.exchange()
       fmt.Println(pair1, pair2, point)
 
       // exchangeThese(pair1, pair2) //wrong
       exchangeThese(&pair1, &pair2)
       fmt.Println(pair1, pair2)
}

運行結果

wKioL1YfYriDNBSMAACXshVCpYE028.jpg

    在本例中,自定義類型StringPair和Point指針實現了接口Exchanger所需的方法,因此該類型的值可以被賦值給接口的值。

    但是,特別注意一點。如果使用exchangeThese(pair1, pair2)會導致編譯錯誤(如下圖),正確寫法應當是exchangeThese(&pair1, &pair2)。這是由於真正滿足接口Exchanger的類型是StringPair指針,而非StringPair。

wKiom1YfY62whJPlAAFdgv-1E-c818.jpg

    在golang值接收者和指針接收者的方法集是不同的。只是golang會智能地解引用和取引用,使得二者的方法集看上去是一樣的。但是,在調用exchangeThese時,就凸顯出二者的不同了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章