interface和nil
golang的nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值,在golang中,nil只能赋值给指针、channel、func、interface、map或slice类型的变量。如果未遵循这个规则,则会引发panic.
在底层,interface作为两个成员来实现,一个类型和一个值.接下来通过编写测试代码来看看interface倒底是什么。
测试代码一:
package main
import (
"fmt"
"reflect"
)
func main() {
var val interface{} = int32(1)
fmt.Println(reflect.TypeOf(val))
val = 50
fmt.Println(reflect.TypeOf(val))
}
我们已经知道接口类型变量底层是作为两个成员来实现,一个是type,一个是data。type用于存储变量的动态类型,data用于存储变量的具体数据。在上面的例子中,第一条打印语句输出的是:int32。这是因为已经显示的将类型为int64的数据1赋值给了interface类型的变量val,所以val的底层结构应该是:(int32, 1)。我们暂且用这种二元组的方式来描述,二元组的第一个成员为type,第二个成员为data。第二条打印语句输出的是:int。这是因为字面量的整数在golang中默认的类型是int,所以这个时候val的底层结构就变成了:(int, 1)
在底层,interface作为两个成员实现,一个类型,一个值。只有在内部值和类型都设置为nil时,interface才为nil。特别的,一个nil接口总是拥有一个nil类型。若我们在一个接口值中存储一个int类型指针,则内部类型将为int,而无论该指针的值是什么:(*int,nil)。因此这个接口值是非nil的,即使该指针值为nil。
看看测试代码二:
package main
import (
"fmt"
)
func main() {
var val interface{} = nil
if val == nil {
fmt.Println("val is nil")
} else {
fmt.Println("val is not nil")
}
}
变量val是interface类型,它的底层结构必然是(type, data)。 由于nil是untyped(无类型),又将nil赋值给了变量val,所以val实际上存储的是(nil, nil)。
测试代码三:
package main
import (
"fmt"
)
func main() {
var val interface{} = (*interface{})(nil)
if val == nil {
fmt.Println("val is nil")
} else {
mt.Println("val is not nil")
}
来看一种特例:(interface{})(nil)。将nil转成interface类型的指针,得到的结果仅仅是空接口类型指针并且它指向无效的地址。上面的代码定义了接口指针类型变量val,它指向无效的地址(0x0),因此val持有无效的数据。但它是有类型的(interface{})。所以val的底层结构应该是:(interface{}, nil)。很显然,无论该指针的值是什么:(interface{}, nil),这样的接口值总是非nil的,即使在该指针的内部为nil。
有疑问加站长微信联系(非本文作者)