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