interface是golang中的精華所在,本文主要理解interface中的幾個關鍵點。
I、interface即是method的集合,也是一種類型
1、interface存在的基本作用就是其定義了一組方法。
我們之所以又說interface是一種類型,可以從三點來理解:首先從其定義形式中的type
關鍵字就可以看出來。另外,函數的形參可以為interface型;最後,interface支撐了go中的多態性,也就是其他類型如果實現了interface中的所有方法,就說類型實現了該interface,這類似於C++中的繼承。
type Inter interface { Get() int Set(int)}
2、go中允許不帶任何方法的interface,這種類型稱為empty interface,由於其不帶任何方法,所以可以說所有的類型都實現了empty interface。
II、interface變數儲存的是實作類別型的值
1、由於interface中只存在方法,而方法的形參就來自於其實作類別型。
package main import "fmt"type Inter interface { Get() int Set(int)}type St struct { Age int }func(s St) Get() int { return s.Age} func(s *St) Set(age int) { s.Age = age }func test(i Inter) { i.Set(10) fmt.Println(i.Get())}func main() { s := St{} test(&s)}
這段代碼中,St實現了Inter,執行test()
,就完成了對Inter的使用。
2、interface的重要用途之一就是體現在test函數的形參上,如果有多個類型實現了interface,這些類型的值都可以直接使用interface的變數儲存。
s := S{}var i Interi = &sfmt.Println(i.Get()) //會自動調用S中關於Get的實現
這也體現了go中的多態性。
III、empty interface
1、空的interface沒有方法,所以所有的類型都實現了empty interface,所以,所有的類型都可以作為empty interface函數的形參:
func doSomething(v interface{}) {}
既然空的interface可以接受任何類型的參數,那麼一個interface{}類型的slice是不是可以接受任何類型的slice呢? --不能!
package main import "fmt"func printAll(vals []interface{}) { for _, val := range vals { fmt.Println(val) }}func main() { names := []string{"hello", "world"} printAll(names)}
執行結果:
這個例子說明go不能將slice轉化成interface{}類型的slice,但是我們可以手動進行轉化:
var interfaceSlice []interface{} = make([]interface{}, len(names)) for i,d := range names { interfaceSlice[i] = d }
執行結果:
IV、receiver的理解
1、go中將定義struct的方法中的func() 中的參數稱為receiver。例如func(s St) Get() int { }
中的s就是Get的receiver。要理解他可以聯想C++中的this指標。
2、我們在上面的例子中調用test函數是test(&s)
,也就是St的指標類型,可以是test(s)
嗎?
調用test(s)
的執行結果如下:
這是一個錯誤的實現,關鍵在於St中Set()
方法的receiver是一個pointer *St
。
interface定義時並沒有規定是閑著的方法receiver是value receiver 還是pointer receiver,如上述例子,當我們使用test(s)
的形式調用,傳遞給test的是s的一份拷貝,在進行s的拷貝到Inter的轉換時,s的拷貝不滿速Set()
方法的receiver是個pointer,也就是沒有實現。
而如果反過來receiver是value,函數用pointer的形式調用:
package main import "fmt"type Inter interface { Get() int Set(int)}type St struct { Age int }func(s St) Get() int { return s.Age} func(s St) Set(age int) { s.Age = age }func test(i Inter) { i.Set(10) fmt.Println(i.Get())}func main() { s := St{} test(&s) test(s)}
執行結果為:
之所以沒能按照我們預期的輸出10 10,是因為傳值不能改變未經處理資料的值。但是代碼是能正常啟動並執行,也就是好說receiver都value receiver,執行代碼無論是pointer還是value都可以正常執行。
再思考一下出現這種現象的原因是什麼呢?
如果是傳入pointer,go可以根據pointer找到對應指向的值,但如果是value,傳入的只能是value的拷貝temp,沒辦法根據value的拷貝temp去找到value原始的地址,這就是為什麼pointer可以對應pointer receiver以及value receiver,但value卻無法滿足pointer receiver。
其實這裡很關鍵的一點就是,實參到形參只是一個拷貝。
【參考】
[1] 理解 Go interface 的 5 個關鍵點