C語言中文網 目錄
首頁 > Go語言教程 > Go語言接口 閱讀:3,699

Go語言接口和類型之間的轉換

Go 語言中使用接口斷言(type assertions)將接口轉換成另外一個接口,也可以將接口轉換為另外的類型。接口的轉換在開發中非常常見,使用也非常頻繁。

類型斷言的格式

類型斷言的基本格式如下:
t := i.(T)
其中,i 代表接口變量,T 代表轉換的目標類型,t 代表轉換后的變量。

如果 i 沒有完全實現 T 接口的方法,這個語句將會觸發宕機。觸發宕機不是很友好,因此上面的語句還有一種寫法:
t,ok := i.(T)
這種寫法下,如果發生接口未實現時,將會把 ok 置為 false,t 置為 T 類型的 0 值。正常實現時,ok 為 true。這里 ok 可以被認為是:i 接口是否實現 T 類型的結果。

將接口轉換為其他接口

實現某個接口的類型同時實現了另外一個接口,此時可以在兩個接口間轉換。

鳥和豬具有不同的特性,鳥可以飛,豬不能飛,但兩種動物都可以行走。如果使用結構體實現鳥和豬,讓它們具備自己特性的 Fly() 和 Walk() 方法就讓鳥和豬各自實現了飛行動物接口(Flyer)和行走動物接口(Walker)。

將鳥和豬的實例創建后,被保存到 interface{} 類型的 map 中。interface{} 類型表示空接口,意思就是這種接口可以保存為任意類型。對保存有鳥或豬的實例的 interface{} 變量進行斷言操作,如果斷言對象是斷言指定的類型,則返回轉換為斷言對象類型的接口;如果不是指定的斷言類型時,斷言的第二個參數將返回 false。例如下面的代碼:
var obj interface = new(bird)
f, isFlyer := obj.(Flyer)
代碼中,new(bird) 產生 *bird 類型的 bird 實例,這個實例被保存在 interface{} 類型的 obj 變量中。使用 obj.(Flyer) 類型斷言,將 obj 轉換為 Flyer 接口。f 為轉換成功時的 Flyer 接口類型,isFlyer 表示是否轉換成功,類型就是 bool。

下面是詳細的代碼(代碼1):
package main

import "fmt"

// 定義飛行動物接口
type Flyer interface {
    Fly()
}

// 定義行走動物接口
type Walker interface {
    Walk()
}

// 定義鳥類
type bird struct {
}

// 實現飛行動物接口
func (b *bird) Fly() {
    fmt.Println("bird: fly")
}

// 為鳥添加Walk()方法, 實現行走動物接口
func (b *bird) Walk() {
    fmt.Println("bird: walk")
}

// 定義豬
type pig struct {
}

// 為豬添加Walk()方法, 實現行走動物接口
func (p *pig) Walk() {
    fmt.Println("pig: walk")
}

func main() {

// 創建動物的名字到實例的映射
    animals := map[string]interface{}{
        "bird": new(bird),
        "pig":  new(pig),
    }

    // 遍歷映射
    for name, obj := range animals {

        // 判斷對象是否為飛行動物
        f, isFlyer := obj.(Flyer)
        // 判斷對象是否為行走動物
        w, isWalker := obj.(Walker)

        fmt.Printf("name: %s isFlyer: %v isWalker: %v\n", name, isFlyer, isWalker)

        // 如果是飛行動物則調用飛行動物接口
        if isFlyer {
            f.Fly()
        }

        // 如果是行走動物則調用行走動物接口
        if isWalker {
            w.Walk()
        }
    }
}
代碼說明如下:
  • 第 6 行定義了飛行動物的接口。
  • 第 11 行定義了行走動物的接口。
  • 第 16 和 30 行分別定義了鳥和豬兩個對象,并分別實現了飛行動物和行走動物接口。
  • 第 41 行是一個 map,映射對象名字和對象實例,實例是鳥和豬。
  • 第 47 行開始遍歷 map,obj 為 interface{} 接口類型。
  • 第 50 行中,使用類型斷言獲得 f,類型為 Flyer 及 isFlyer 的斷言成功的判定。
  • 第 52 行中,使用類型斷言獲得 w,類型為 Walker 及 isWalker 的斷言成功的判定。
  • 第 57 和 62 行,根據飛行動物和行走動物兩者是否斷言成功,調用其接口。

代碼輸出如下:
name: pig isFlyer: false isWalker: true
pig: walk
name: bird isFlyer: true isWalker: true
bird: fly
bird: walk

將接口轉換為其他類型

在代碼 1 中,可以實現將接口轉換為普通的指針類型。例如將 Walker 接口轉換為 *pig 類型,請參考下面的代碼:
p1 := new(pig)

var a Walker = p1
p2 := a.(*pig)

fmt.Printf("p1=%p p2=%p", p1, p2)
對代碼的說明如下:
  • 第 3 行,由于 pig 實現了 Walker 接口,因此可以被隱式轉換為 Walker 接口類型保存于 a 中。
  • 第 4 行,由于 a 中保存的本來就是 *pig 本體,因此可以轉換為 *pig 類型。
  • 第 6 行,對比發現,p1 和 p2 指針是相同的。

如果嘗試將上面這段代碼中的 Walker 類型的 a 轉換為 *bird 類型,將會發出運行時錯誤,請參考下面的代碼:
p1 := new(pig)

var a Walker = p1
p2 := a.(*bird)
運行時報錯:

panic: interface conversion: main.Walker is *main.pig, not *main.bird

報錯意思是:接口轉換時,main.Walker 接口的內部保存的是 *main.pig,而不是 *main.bird。

因此,接口在轉換為其他類型時,接口內保存的實例對應的類型指針,必須是要轉換的對應的類型指針。

總結

接口和其他類型的轉換可以在 Go 語言中自由進行,前提是已經完全實現。

接口斷言類似于流程控制中的 if。但大量類型斷言出現時,應使用更為高效的類型分支 switch 特性。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo