7.1. 空接口-万能封包器
在凹语言中,最简单的接口是空接口,既 interface {}
,声明接口类型变量的方法跟其它类型一致,例如下面的代码声明了一个名为 i
的空接口变量:
i: interface{}
习惯上我们一般将 接口类型变量 称为 接口值。空接口有一个非常独特的特性:任何类型的值都可以赋值给空接口值,例如下面的操做全是合法的:
iface: interface{}
iface = 777 // 无类型整数赋值给空接口
iface = 13.14 // 无类型浮点数赋值给空接口
iface = "你好,空接口" // 字符串赋值给空接口
i: i64 = 58372665865
iface = i // 64位整数赋值给空接口
// 匿名结构体赋值给空接口:
iface = struct{name: string; age: i32}{name: "凹语言", age: 1}
这种赋值行为执行的是传值操作,相当于在接口值内复制了一份原始数据的拷贝,这份拷贝被称为接口值的具体值,具体值的类型被称为具体类型。
那么如何判断一个已被赋值的接口值所持有的具体类型?如何读取具体值?这就需要用到类型断言语法,它的一般形式为:
v, ok = iface.(Type) // 断言iface的具体类型是否为Type
其中 v
是类型为 Type
的值, ok
是 bool
型值,该语句执行后,若 ok
为 true
,则表明接口值 iface
的具体类型确实是 Type
,并且其具体值将被赋予 v
;否则表明 iface
的具体类型不为 Type
。实际示例如下:
// 版权 @2021 凹语言 作者。保留所有权利。
type T1 struct {
a: i32
}
func main {
ival: i32 = 777
printConcrete(ival) // i32: 777
printConcrete("你好凹语言") // string: 你好凹语言
v1 := T1{a: 42}
printConcrete(v1) // T1, T1.a: 42
printConcrete(13.14) // 未知类型
}
func printConcrete(iface: interface{}) {
ok: bool
i: i32
s: string
t: T1
i, ok = iface.(i32)
if ok {
println("i32:", i)
return
}
s, ok = iface.(string)
if ok {
println("string:", s)
return
}
t, ok = iface.(T1)
if ok {
println("T1, T1.a:", t.a)
return
}
println("未知类型")
}
在函数 printConcrete
内,通过接口类型断言,可以动态的判断传入的空接口值的具体类型,并获取其具体值。由于函数内未进行浮点数断言,因此输入浮点数时会输出“未知类型”。
注意函数 printConcrete
的参数类型为空接口(interface{}
),在 main
函数中调用它时,实际上执行了隐式转换(拷贝),比如语句 printConcrete(ival)
实际上等价于:
iface: interface{} = ival
printConcrete(iface)
凹语言在绝大多数情况下不允许隐式类型转换,但接口是个例外。当函数参数类型为接口时,若调用方填入的实参是具体类型,则编译器会自动执行赋值转换的操作。
如果接口值的具体类型存在多种可能,那么使用多个类型断言加条件判断的方法无疑很累赘,在这种场景下,可以使用switch...case...
格式的分支类型断言,例如上述 printConcrete
函数可以改写为:
func printConcrete(iface: interface{}) {
//分支类型断言
switch v := iface.(type) {
case i32:
println("i32:", v) // v是iface的具体值,该分支下,其类型为 i32,下同
case string:
println("string:", v)
case T1:
println("T1, T1.a:", v.a)
default:
println("未知类型")
}
}
其中 iface.(type)
是固定写法,后续每个 case
分支表示具体类型满足该分支条件。
任何类型的值都可以赋予空接口,它在凹语言中实际起到了万能封包器的作用,经常用于在函数间传递类型会动态变化的值。
本文中“空接口”指
interface{}
,既方法集为空的接口类型,下一节中的“非空接口”指方法集不为空的接口类型;当我们要描述值为0的接口值时,将使用“0值接口”,或“nil接口”,请注意区分。