类型断言是一个作用在接口值上的操作,写出来类似于x.(T),其中x是一个接口类型的表达式,而T是一个类型(称为断言类型)。类型断言会检查作为操作数的动态类型是否满足指定的断言类型,这有两种可能:
1.如果T是一个具体类型,那么类型断言会检查x的动态类型是否为T。如果是则检查成功,类型断言的结果就是x的动态值,类型就是T,如果不是则检查失败。
2.如果T是一个接口类型,那么类型断言会检查x的动态类型是否满足T。如果满足则检查成功,但动态值并没有提取出来,结果仍然是一个接口值,接口值的类型和值部分也没有变更,只是结果的类型为接口类型T。换句话说,类型断言是一个接口值表达式,从一个接口类型变为拥有另外一套方法的接口类型(通常是方法数量增多),但保留了接口值中的动态类型和动态值部分。
var w io.Writer
w = os.Stdout //w为io.Writer接口类型,其动态类型为*os.File,值为os.Stdout的副本
rw := w.(io.Writer) //成功,io.Writer符合w的接口
w = new(ByteCounter) //w的动态类型变为[]byte
rw = w.(io.ReadWriter) //失败,*ByteCounter不符合w的接口
无论哪种类型作为断言类型,如果操作数是一个空接口值,类型断言都会失败并触发panic。另外,如果类型断言出现在需要两个结果的赋值表达式中,那么断言不会再失败时崩溃,而是会多返回一个布尔型的返回值来指示断言是否成功。
var w io.Writer = os.Stdout
f, ok = w.(*os.File) //成功,ok = true, f == os.Stdout
b, ok = w.(*bytes.Buffer) //失败,ok = false, b == nil
在介绍Go语言接口的文章:https://blog.youkuaiyun.com/qq_37653144/article/details/86925152中,提到过通过类型断言来实现对空接口实际值的判断:
func judgeType(a interface{}) {
switch a.(type) {
case int:
fmt.Println("The type of a is int")
case string:
fmt.Println("The type of a is string")
case float64:
fmt.Println("The type of a is float64")
//...
default:
fmt.Println("Unknow type")
}
}