前言
在很多情况下,接口类型没有办法直接作为值来使用,或者说我们需要检查某个接口变量是否为我们期望的类型,就需要先使用类型断言 (type assertion)进行类型判断以及转换。
基本用法
断言语句
一个类型断言语句检查它操作对象的动态类型是否和断言的类型匹配。
value, ok := x.(T)
各变量含义为:
x :要判断的变量,必须为接口类型
T :被判断的目标类型
value :成功转换后的值,动态类型
ok:是否为目标类型,bool 类型
需要注意的是,如果 x == nil,那么断言一定失败。
如果在已经确定 x 就是 T 类型的前提下,也可以省略 ok,但是只要类型不匹配,就会报 panic
value := x.(T)
断言类型种类
类型断言语句中,T 可以是具体类型也可以是接口类型。
具体类型
如果 T 是一个具体类型,那么类型断言会检查 x 的动态类型是否和 T 相同,如果相同,ok = true,value 为转换后的值,如果不相同,ok = false 且 value = nil,但是需要注意 x 必须为接口类型,否则编译会失败,使用示例如下:
// 错误用法
// combile error: invalid type assertion: num.(int) (non-interface type int on left) num := 10 if v, ok := num.(int); ok { fmt.Println(v) } // 正确用法 if v, ok := interface{}(num).(int); ok { fmt.Println(v) // out: 10 }
接口类型
如果 T 是一个接口类型,那么类型断言会检查 x的动态类型是否满足 T,即检查 x 是否实现了 T 接口。如果这个检查成功了,x 的动态值不会被提取,返回值是一个类型为 T 的接口值。换句话说,对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常可以使用更多的方法),但是它保护了接口值内部的动态类型和值。
var w io.Writer // w 只能调用 Write 方法 w = os.Stdout rw := w.(io.ReadWriter) // success: *os.File has both Read and Write // rw 可以使用 Read 和 Write 方法
如果 x 不满足 T,就会 panic
type HelloInterface interface { Hello() } var w io.Writer = os.Stdout hello := w.(HelloInterface) // panic: interface conversion: *os.File is not interfacetest.HelloInterface: missing method Hello
type switch
实际使用中,通常会将类型断言与 switch 结合来判断某个变量或者参数的类型,这时候 T 为 type,如官网给出的例子:
switch i := x.(type) { case nil: printString("x is nil") // type of i is type of x (interface{}) case int: printInt(i) // type of i is int case float64: printFloat64(i) // type of i is float64 case func(int) float64: printFunction(i) // type of i is func(int) float64 case bool, string: printString("type is bool or string") // type of i is type of x bool or string default: printString("don't know the type") // type of i is type of x (interface{}) }
基于断言识别错误类型
实际的开发过程中,通常会自定义一些扩展的错误类型来承载比原生 error 更复杂的信息,如:
type GError struct { Code string `json:"code"` Message string `json:"message"` } func (g GError) Error() string { return fmt.Sprintf("Code:%s, Message:%s", g.Code, g.Message) } func (g GError) GetCode() string { return g.Code } func (g GError) GetMessage() string { return g.Message }
GError 类型实现了 error 接口,那么当我们接收到一个错误,可以用类型断言来识别是否是 GError,并且做出对应的处理。
func callapi() *rest.GError { } err := callapi() if err != nil { if gErr, ok := err.(*rest.GError); ok { fmt.Println(gErr.Error()) } else { fmt.Println("system error") }
无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。