Go 的类型断言
Contents
备注:本章是对《Go 程序设计语言》第 7 章的一些笔记,仅供参考。
定义
类型断言(type assertion)是一个作用在 接口值 上的操作,写出来的形式为:
x.(T)
x 是一个接口类型的表达式
T 是一个类型(断言类型)
类型断言会检查作为操作数的动态类型是否满足指定的断言类型。
形式
此处有两种可能:
-
断言类型 T 是一个具体类型
此时类型断言会检查 x 的动态类型是否就是 T。如果检查成功,类型断言的结果就是 x 的动态值,类型当然就是 T。如果检查失败,操作崩溃。即从操作数中把具体类型的值提取出来的操作。
如:
1 2 3 4
var w io.Writer // 接口类型 w = os.Stdout f := w.(*os.File) // 成功,f == os.Stdout c := w.(*bytes.Buffer) // 崩溃:接口持有的是 *os.File,不是 *bytes.Buffer
-
断言类型 T 是一个接口类型
此时类型断言会检查 x 的动态类型是否满足 T。如果检查成功,动态值并没有提取出来,结果仍然是一个接口值,接口值的类型和值部分也没有变更,只是结果的类型为接口类型 T。
换句话说,类型断言是一个接口值表达式,从一个接口类型变为拥有另外一套方法的接口类型(通常是方法数量增多),但保留了接口值中的动态类型和动态值部分。
如:
1 2 3 4 5
var w io.Writer w = os.Stdout rw := w.(io.ReadWriter) // 成功 w = new(ByteCounter) rw = w.(io.ReadWriter) // 崩溃,*ByteCounter 没有 Read 方法
应用
一般来说,我们将类型断言应用在需要两个结果的赋值表达式中,那么类型断言不会在失败时崩溃,而是会多返回一个布尔型的返回值来指示断言是否成功。
|
|
按照惯例,一般会把第二个返回值赋给一个名为 ok
的变量。如果操作失败,ok
为 false
。
经常可以写出如下代码:
|
|
1. 使用类型断言来识别错误
如以下这个例子所展示的:
|
|
2. 通过接口类型来查询特性
如果接口符合某种特性(接口),就执行某段逻辑。
3. 类型分支
接口有两种不同的风格:
-
接口上的各种方法突出了满足这个接口的具体类型之间的相似性,但隐藏了各个具体类型的布局和各自特有的功能。这种风格强调方法,而不是具体类型。
如
io.Reader
,io.Writer
,fmt.Stringer
,sort.Interface
等。 -
第二种风格则充分利用了接口值能够容纳多种具体类型的能力,它把接口作为这些类型的联合(union)来使用。类型断言用来在运行时区分这些类型并分别处理。
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
func sqlQuote(x interface{}) string { if x == nil { return "NULL" } else if _, ok := x.(int); ok { return fmt.Sprintf("%d", x) } else if _, ok := x.(uint); ok { return fmt.Sprintf("%d", x) } else if b, ok := x.(bool); ok { if b { return "TRUE" } return "FALSE" } else if s, ok := x.(string); ok { return sqlQuoteString(s) // (not shown) } else { panic(fmt.Sprintf("unexpected type %T: %v", x, x)) } }
可将其用 switch-case 的方式改写成:
1 2 3 4 5 6 7 8
// 将每个分支中提取出来的原始值绑定到一个新的变量 switch x := x.(type) { case nil: // ... case int, uint: // ... case bool: // ... case string: // ... default: // ... }