# 11.3 類型斷言:如何檢測和轉換接口變量的類型

一個接口類型的變量 varI 中可以包含任何類型的值,必須有一種方式來檢測它的 動態 類型,即運行時在變量中存儲的值的實際類型。在執行過程中動態類型可能會有所不同,但是它總是可以分配給接口變量本身的類型。通常我們可以使用 類型斷言 來測試在某個時刻 varI 是否包含類型 T 的值:

v := varI.(T)       // unchecked type assertion

varI 必須是一個接口變量,否則編譯器會報錯:invalid type assertion: varI.(T) (non-interface type (type of varI) on left)

類型斷言可能是無效的,雖然編譯器會盡力檢查轉換是否有效,但是它不可能預見所有的可能性。如果轉換在程序運行時失敗會導致錯誤發生。更安全的方式是使用以下形式來進行類型斷言:

if v, ok := varI.(T); ok {  // checked type assertion
    Process(v)
    return
}
// varI is not of type T

如果轉換合法,vvarI 轉換到類型 T 的值,ok 會是 true;否則 v 是類型 T 的零值,okfalse,也沒有運行時錯誤發生。

應該總是使用上面的方式來進行類型斷言

多數情況下,我們可能只是想在 if 中測試一下 ok 的值,此時使用以下的方法會是最方便的:

if _, ok := varI.(T); ok {
    // ...
}

示例 11.4 type_interfaces.go

package main

import (
    "fmt"
    "math"
)

type Square struct {
    side float32
}

type Circle struct {
    radius float32
}

type Shaper interface {
    Area() float32
}

func main() {
    var areaIntf Shaper
    sq1 := new(Square)
    sq1.side = 5

    areaIntf = sq1
    // Is Square the type of areaIntf?
    if t, ok := areaIntf.(*Square); ok {
        fmt.Printf("The type of areaIntf is: %T\n", t)
    }
    if u, ok := areaIntf.(*Circle); ok {
        fmt.Printf("The type of areaIntf is: %T\n", u)
    } else {
        fmt.Println("areaIntf does not contain a variable of type Circle")
    }
}

func (sq *Square) Area() float32 {
    return sq.side * sq.side
}

func (ci *Circle) Area() float32 {
    return ci.radius * ci.radius * math.Pi
}

輸出:

The type of areaIntf is: *main.Square
areaIntf does not contain a variable of type Circle

程序行中定義了一個新類型 Circle,它也實現了 Shaper 接口。 t, ok := areaIntf.(*Square); ok 測試 areaIntf 裏是否一個包含 'Square' 類型的變量,結果是確定的;然後我們測試它是否包含一個 'Circle' 類型的變量,結果是否定的。

備註

如果忽略 areaIntf.(*Square) 中的 * 號,會導致編譯錯誤:impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)

鏈接

results matching ""

    No results matching ""