本文最后更新于一年前或更久前,其中的信息可能已经有所发展或是发生改变。
从动态类型讲起
众所周知,GO是一门静态语言,一个变量的类型在声明后就无法再改变为其他类型,但是真的没有办法突破这样的限制吗?比如我想根据类型的不同函数能打印不同的效果,传入的参数是不确定的(不确定具体类型),那么如何才能根据变量的类型打印对应呢?
空接口的出现
空接口的内部实现保存了对象的类型和指针。使用空接口保存一个数据的过程会比直接用数据对应类型的变量保存稍慢。因此在开发中,应在需要的地方使用空接口,而不是在所有地方使用空接口。
将值保存到空接口
var any interface{}
any = 1
fmt.Println(any)
any = "hello"
fmt.Println(any)
any = false
fmt.Println(any)
代码输出:
1
hello
false
在每次赋值之后,any
的变量类型始终没有变化,始终为interface{}
interface{}
中保存了当前变量的实际类型和实际值
类型断言
类型断言是一个用在接口值上的操作,可以用来检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型
在Golang中使用功接口断言的语法格式如下(还有一个不接收ok
值的版本,但是一旦不满足对应类型,将使得代码panic
,严重影响程序健壮性,所以在此处不介绍):
value, ok := x.(T)
其中,x 表示一个接口的类型,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)
fmt.Print(value, ",", ok)
}
在这里,空接口x
的类型为int
所以满足条件,运行结果将如下:10,true
回到fmt.Println()
既然到这里,我们就能明白fmt.Println()的设计原理了:
我们可以结合switch
判断空接口参数的具体类型,然后根据具体类型进行不同的print
效果,比如我们在下面写一个判断类型的代码:
package main
import (
"fmt"
)
func main() {
var a int
a = 10
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
未来展望:泛型
Golang 1.18(发布于2022-03-15)最终向Go语言中引入了泛型,此文不再介绍,请参考2️⃣
Reference:
1️⃣ http://c.biancheng.net/view/84.html
2️⃣ https://segmentfault.com/a/1190000041634906 -> 1.18新加入的泛型