?# 11.3 類型斷言:如何檢測和轉換接口變量的類型
一個接口類型的變量 varI
中可以包含任何類型的值,必須有一種方式來檢測它的 動態(tài) 類型,即運行時在變量中存儲的值的實際類型。在執(zhí)行過程中動態(tài)類型可能會有所不同,但是它總是可以分配給接口變量本身的類型。通常我們可以使用 類型斷言 來測試在某個時刻 varI
是否包含類型 T
的值:
v := varI.(T) // unchecked type assertion
varI 必須是一個接口變量,否則編譯器會報錯:invalid type assertion: varI.(T) (non-interface type (type of varI) on left)
。
類型斷言可能是無效的,雖然編譯器會盡力檢查轉換是否有效,但是它不可能預見所有的可能性。如果轉換在程序運行時失敗會導致錯誤發(fā)生。更安全的方式是使用以下形式來進行類型斷言:
if v, ok := varI.(T); ok { // checked type assertion
Process(v)
return
}
// varI is not of type T
如果轉換合法,v
是 varI
轉換到類型 T
的值,ok
會是 true
;否則 v
是類型 T
的零值,ok
是 false
,也沒有運行時錯誤發(fā)生。
應該總是使用上面的方式來進行類型斷言。
多數(shù)情況下,我們可能只是想在 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
,它也實現(xiàn)了 Shaper
接口。 if t, ok := areaIntf.(*Square); ok
測試 areaIntf
里是否有一個包含 *Square
類型的變量,結果是確定的;然后我們測試它是否包含一個 *Circle
類型的變量,結果是否定的。
備注
如果忽略 areaIntf.(*Square)
中的 *
號,會導致編譯錯誤:impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)
。