golang type assertion 类型断言 类型判断以及转换

前言

在很多情况下,接口类型没有办法直接作为值来使用,或者说我们需要检查某个接口变量是否为我们期望的类型,就需要先使用类型断言 (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")
}


鼎云博客
  • 最新评论
  • 总共0条评论