理解go中interface關鍵點
interface是golang中的精華所在,本文主要理解interface中的幾個關鍵點。
interface即是method的集合,也是一種類型
- interface存在的基本作用就是其定義了一組方法。
我們之所以又說interface是一種類型,可以從三點來理解:首先從其定義形式中的type關鍵字就可以看出來。另外,函數的形參可以為interface型;最后,interface支撐了go中的多態性,也就是其他類型如果實現了interface中的所有方法,就說類型實現了該interface,這類似于C++中的繼承。
type Inter interface {
Get() int
Set(int)
}
- go中允許不帶任何方法的interface,這種類型稱為empty interface,由于其不帶任何方法,所以可以說所有的類型都實現了empty interface。
interface變量存儲的是實現類型的值
- 由于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的使用。
- interface的重要用途之一就是體現在test函數的形參上,如果有多個類型實現了interface,這些類型的值都可以直接使用interface的變量存儲。
s := S{}
var i Inter
i = &s
fmt.Println(i.Get()) //會自動調用S中關于Get的實現
這也體現了go中的多態性。
empty interface
- 空的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
}
執行結果:
receiver的理解
- go中將定義struct的方法中的func() 中的參數稱為receiver。例如func(s St) Get() int { }中的s就是Get的receiver。要理解他可以聯想C++中的this指針。
- 我們在上面的例子中調用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。
其實這里很關鍵的一點就是,實參到形參只是一個拷貝。