35. Go 語言中關於接口的三個規則

Hi,大家好,我是明哥。

在自己學習 Golang 的這段時間裏,我寫了詳細的學習筆記放在我的個人微信公衆號 《Go編程時光》,對於 Go 語言,我也算是個初學者,因此寫的東西應該會比較適合剛接觸的同學,如果你也是剛學習 Go 語言,不防關注一下,一起學習,一起成長。

我的在線博客:http://golang.iswbm.com 我的 Github:github.com/iswbm/GolangCodingTime

1. 對方法的調用限制

接口是一組固定的方法集,由於靜態類型的限制,接口變量有時僅能調用其中特定的一些方法。

請看下面這段代碼

package main

import "fmt"

type Phone interface {
    call()
}

type iPhone struct {
    name string
}

func (phone iPhone)call()  {
    fmt.Println("Hello, iPhone.")
}

func (phone iPhone)send_wechat()  {
    fmt.Println("Hello, Wechat.")
}

func main() {
    var phone Phone
    phone = iPhone{name:"ming's iphone"}
    phone.call()
    phone.send_wechat()
}

我定義了一個 Phone 的接口,只要求實現 call 方法即可,也就是隻要能打電話的設備就是一個電話(好像是一句沒用的廢話)。

然後再定義了一個 iPhone 的結構體,該結構體接收兩個方法,一個是打電話( call 函數),一個是發微信(send_wechat 函數)。

最後一步是關鍵,我們定義了一個 Phone 接口類型的 phone 對象,該對象的內容是 iPhone 結構體。然後我們調用該對象的 call 方法,一切正常。

但是當你調用 phone.send_wechat方法,程序會報錯,提示我們 Phone 類型的方法沒有 send_wechat 的字段或方法。

# command-line-arguments
./demo.go:30:10: phone.send_wechat undefined (type Phone has no field or method send_wechat)

原因也很明顯,因爲我們的phone對象顯示聲明爲 Phone 接口類型,因此 phone調用的方法會受到此接口的限制。

那麼如何讓 phone 可以調用 send_wechat 方法呢?

答案是可以不顯示的聲明爲 Phone接口類型 ,但要清楚 phone 對象實際上是隱式的實現了 Phone 接口,如此一來,方法的調用就不會受到接口類型的約束。

修改 main 方法成如下

func main() {
    phone := iPhone{name:"ming's iphone"}
    phone.call()
    phone.send_wechat()
}

運行後,一切正常,沒有報錯。

Hello, iPhone.
Hello, Wechat.

2. 調用函數時的隱式轉換

Go 語言中的函數調用都是值傳遞的,變量會在方法調用前進行類型轉換。

比如下面這段代碼


import (
    "fmt"
)

func printType(i interface{})  {

    switch i.(type) {
    case int:
        fmt.Println("參數的類型是 int")
    case string:
        fmt.Println("參數的類型是 string")
    }
}

func main() {
    a := 10
    printType(a)
}

如果你運行後,會發現一切都很正常

參數的類型是 int

但是如果你把函數內的內容搬到到外面來

package main

import "fmt"


func main() {
    a := 10

    switch a.(type) {
    case int:
        fmt.Println("參數的類型是 int")
    case string:
        fmt.Println("參數的類型是 string")
    }
}

就會有意想不到的結果,居然報錯了。

# command-line-arguments
./demo.go:9:5: cannot type switch on non-interface value a (type int)

這個操作會讓一個新人摸不着頭腦,代碼邏輯都是一樣的,爲什麼一個不會報錯,一個會報錯呢?

原因其實很簡單。

當一個函數接口 interface{} 空接口類型時,我們說它可以接收什麼任意類型的參數(江湖上稱之爲無招勝有招)。

當你使用這種寫法時,Go 會默默地爲我們做一件事,就是把傳入函數的參數值(注意:Go 語言中的函數調用都是值傳遞的)的類型隱式的轉換成 interface{} 類型。

如何進行接口類型的顯示轉換

上面瞭解了函數中 接口類型的隱式轉換後,你的心裏可能開始有了疑問了,難道我使用類型斷言,只能通過一個接收空接口類型的函數才能實現嗎?

答案當然是 No.

如果你想手動對其進行類型轉換,可以像下面這樣子,就可以將變量 a 的靜態類型轉換爲 interface{} 類型然後賦值給 b (此時 a 的靜態類型還是 int,而 b 的靜態類型爲 interface{})

var a int = 25
b := interface{}(a)

知道了方法後,將代碼修改成如下:

package main

import "fmt"


func main() {
    a := 10

    switch interface{}(a).(type) {
    case int:
        fmt.Println("參數的類型是 int")
    case string:
        fmt.Println("參數的類型是 string")
    }
}

運行後,一切正常。

參數的類型是 int

3. 類型斷言中的隱式轉換

上面我們知道了,只有靜態類型爲接口類型的對象纔可以進行類型斷言。

而當類型斷言完成後,會返回一個靜態類型爲你斷言的類型的對象,也就是說,當我們使用了類型斷言,Go 實際上又會默認爲我們進行了一次隱式的類型轉換。

驗證方法也很簡單,使用完一次類型斷言後,對返回的對象再一次使用類型斷言,Goland 立馬就會提示我們新對象 b 不是一個接口類型的對象,不允許進行類型斷言。


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