Go语言断言和类型查询的实现
作者:242030
1、类型断言
类型断言(Type Assertion)是一个使用在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。
在Go语言中类型断言的语法格式如下:
// i.(TypeNname) value, ok := x.(T)
其中,x 表示一个接口的类型,如果是具体类型变量,则编译器会报non-interface type xxx on left
,T 表示一个具体的类型(也可为接口类型)。
该断言表达式会返回 x 的值(也就是 value)和一个布尔值(也就是 ok),可根据该布尔值判断 x 是否为 T 类型:
如果 T 是具体某个类型,类型断言会检查 x 的动态类型是否等于具体类型 T。如果检查成功,类型断言返回的结果是 x 的动态值,其类型是 T。
如果 T 是接口类型,类型断言会检查 x 的动态类型是否满足 T。如果检查成功,x 的动态值不会被提取,返回值是一个类型为 T 的接口值。
无论 T 是什么类型,如果 x 是 nil 接口值,类型断言都会失败。
示例代码如下:
package main import ( "fmt" ) func main() { var x interface{} x = 10 value, ok := x.(int) // 10,true fmt.Print(value, ",", ok) }
运行结果如下:
# 程序结果
10,true
需要注意如果不接收第二个参数也就是上面代码中的 ok,断言失败时会直接造成一个 panic,如果 x 为 nil 同样也会 panic。
示例代码如下:
package main import ( "fmt" ) func main() { var x interface{} x = "Hello" value := x.(int) fmt.Println(value) }
运行结果如下:
# 输出结果
panic: interface conversion: interface {} is string, not int
接口断言通常可以使用 comma,ok 语句来确定接口是否绑定某个实例类型,或者判断接口绑定的实例类型是否实现另一个接口。
re,ok := body.(io.ReadCloser) if,ok := r.Body.(*maxBytesReader);
2、类型查询
接口类型查询的语法格式如下:
switch v := i.(type){ case typel: XXXX case type2: XXXX default: XXXX }
接口查询有两层语义,一是查询一个接口变量底层绑定的底层变量的具体类型是什么,二是查询接口变量绑定的底
层变量是否还实现了其他接口。
(1)、i 必须是接口类型
具体类型实例的类型是静态的,在类型声明后就不再变化,所以具体类型的变量不存在类型查询,类型查询一定是
对一个接口变量进行操作。也就是说,上文中的i必须是接口变量,如果i是未初始化接口变量,则v的值是 nil。
package main import ( "fmt" "io" ) func main() { var i io.Reader //此处i是为未初始化的接口变量,所以v为nil switch v := i.(type) { case nil: //<nil> fmt.Printf("%T\n", v) default: fmt.Printf("default") } }
(2)、case 字句后面可以跟非接口类型名,也可以跟接口类型名,匹配是按照 case 子句的顺序进行的。
如果 case 后面是一个接口类型名,且接口变量i绑定的实例类型实现了该接口类型的方法,则匹配成,v的类型是接口类型,v底层绑定的实例是i绑定具体类型实例的副本。
如果 case 后面是一个具体类型名,且接口变量i绑定的实例类型和该具体类型相同,则匹配成功,此时v 就是该具体类型变量,v的值是i绑定的实例值的副本。
如果 case 后面跟着多个类型,使用逗号分隔,接口变量i绑定的实例类型只要和其中一个类型匹配,则直接使用o赋值给v,相当于 v:=o。这个语法有点奇怪,按理说编译器不应该允许这种操作,语言实现者可能想让type switch 语句和普通的 switch 语句保持一样的语法规则,允许发生这种情况。
如果所有的case字句都不满足,则执行 default 语句,此时执行的仍然是 v:=o,最终v的值是o。此时使用v没有任何意义。
fallthrough 语句不能在 Type Switch 语句中使用。
package main import ( "fmt" "io" "log" "os" ) func main() { var i io.Reader // 此处i是为未初始化的接口变量,所以v为nil switch v := i.(type) { case nil: // <nil> fmt.Printf("%T\n", v) default: fmt.Printf("default") } f, err := os.OpenFile("notes.txt", os.O_RDWR|os.O_CREATE, 0755) if err != nil { log.Fatal(err) } defer f.Close() i = f switch v := i.(type) { // i的绑定的实例是*osFile类型,实现了io.ReadWriter接口,所以下面case匹配成功 case io.ReadWriter: // v是io.ReadWriter接口类型,所以可以调用Write方法 v.Write([]byte("io.ReadWriter\n")) // Type Switch Result: io.ReadWriter fmt.Println("Type Switch Result: io.ReadWriter") // 由于上一个case已经匹配,就算这个case也匹配,也不会走到这里 case *os.File: v.Write([]byte("*os.File\n")) fmt.Println("Type Switch Result: *os.File") //这里可以调用具体类型方法 v.Sync() default: fmt.Println("Type Switch Result: unknown") return } switch v := i.(type) { // 匹配成功,v的类型就是具体类型*os.File case *os.File: v.Write([]byte("*os.File\n")) // Type Switch Result: *os.File fmt.Println("Type Switch Result: *os.File") v.Sync() //由于上一个case已经匹配,就算这个case也匹配,也不会走到这里 case io.ReadWriter: //v是io.ReadWriter接口类型,所以可以调用Write方法 v.Write([]byte("io.ReadWriter\n")) fmt.Println("Type Switch Result: io.ReadWriter") default: fmt.Println("Type Switch Result: unknown") return } switch v := i.(type) { //多个类型,f满足其中任何一个就算匹配 case *os.File, io.ReadWriter: // 此时相当于执行力v := i ,v和i是等价的,使用v没有意义。 if v == i { // true fmt.Println(true) } default: return } }
# 程序输出
<nil>
Type Switch Result: io.ReadWriter
Type Switch Result: *os.File
true
package main import ( "fmt" ) func main() { var a int a = 10 // the type of a is int getType(a) } func getType(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 float") default: fmt.Println("unknown type") } }
# 程序输出
the type of a is int
到此这篇关于Go语言断言和类型查询的实现的文章就介绍到这了,更多相关Go语言断言和类型查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!