介绍
Golang 语言是一门静态类型的编程语言,我们在编写代码时,为了提升代码的灵活性,有时会使用空接口类型,对于空接口类型的变量,一般会通过类型断言判断变量的类型,而且可能还会遇到需要类型转换的场景。本文我们就来介绍一下类型断言、类型转换和类型选择。
编程技巧
类型断言
类型断言提供了访问接口类型值的底层具体值的方式,这里需要注意的是被类型断言的值必须是接口类型的值。类型断言返回两个值,被断言的接口类型值的底层具体值和一个表示是否断言成功的布尔类型的值。
t, ok := i.(T)
我们在上一篇介绍变量的文章中,介绍过类型推断,并通过列举一个示例证明使用类型推断可以使代码提升可维护性和健壮性。但是使用类型推断的代码也有缺点,我们先看一下这两段代码。
未使用类型断言:
func main () { id := getVal(1) // id := getVal("a") fmt.Println(id) } func getVal (val interface{}) interface{} { return val }
使用类型断言:
func main () { id := getVal(1) // id := getVal("a") id, ok := id.(int); if !ok { err := errors.New("illegal parameter") fmt.Println(err) return } fmt.Println(id) } func getVal (val interface{}) interface{} { return val }
阅读上面这两段代码,main 函数中都是通过调用 getVal()
函数,使用类型推断的方式给变量 id 赋值。不同的是 main 函数,第一段代码中未使用类型断言,第二段代码中使用了类型断言。所以第一段代码的 main 函数如果调用 getVal()
函数时传递非整型参数,也可以正常输出;第二段代码的 main 函数如果调用 getVal()
函数时传递非整型参数,将会被断言代码拦截。
类型转换
类型转换的表达式 T(v)
,将 v 转换为类型 T。关于类型转换,我们需要注意的“坑”如下所示:
整型之间的转换:
func main () { var a int a = 128 b := int8(a) fmt.Println(b) // -128 }
阅读上面这段代码,int 类型的变量 a 赋值为 128,将变量 a 转换为 int8 类型的变量 b,输出变量 b 的结果是 -128,原因是 int 类型和 int8 类型的取值范围不同。
浮点型转换为整型:
func main () { var a float64 a = 3.1415926 b := int(a) fmt.Println(b) // 3 }
阅读上面这段代码,浮点型 float64 的变量 a,转换为 int 类型的变量 b,输出结果是 3,变量 a 值的小数部分被截掉了。
整型转换为字符串类型:
func main () { var a int a = -1 b := string(a) // conversion from int to string yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?) fmt.Println(b) // � }
阅读上面这段代码,int 类型变量 a,转换为 string 类型变量 b,变量 b 的输出结果是 �
,表示未知字符,原因是 Unicode 码点(code point)中没有 -1。
字符串类型和切片(字节切片和字符切片)类型互相转换:
func main () { var a string a = "编程" b := []byte(a) fmt.Println(b) // [231 188 150 231 168 139] c := []rune(a) fmt.Println(c) // [32534 31243] }
阅读上面这段代码,将字符串类型的变量 a 分别转换为字节切片类型变量 b 和字符切片类型变量 c,它们的输出结果不同,原因是 UTF-8 编码一个中文汉字是 3 个字节, 3 个字节代表 1 个字符。而字符切片类型的元素本身就是 Unicode 字符。
类型选择
最后我们再介绍一下类型选择(type switch),也有人翻译为类型切换。它是一种按照顺序从几个类型断言中选择分支的结构,类型选择与 switch 语句类似,但是类型选择中的 case 不是值,而是值的类型。
func main () { var a interface{} // a = 1 // a = "golang" a = false switch val := a.(type) { case int: fmt.Printf("val:%d type:%T\n", val, val) case string: fmt.Printf("val:%s type:%T\n", val, val) default: fmt.Printf("unknow type:%T\n", val) } }
阅读上面这段代码,我们使用 type switch 判断空接口类型变量 a 的实际数据类型是否为我们预定义的类型之一。
03
总结
本文我们介绍了 Golang 语言类型相关的编程技巧,通过使用类型断言、类型转换和类型选择,可以使我们的代码更加灵活。
推荐阅读:
参考资料:
https://tour.go-zh.org/methods/15
https://golang.org/ref/spec#Conversions
https://tour.go-zh.org/basics/13
https://liyucang-git.github.io/2019/06/17/彻底弄懂Unicode编码/
https://tour.go-zh.org/methods/16