理解go中interface關鍵點

理解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。

其實這里很關鍵的一點就是,實參到形參只是一個拷貝。

image
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容