interface和nil
golang的nil在概念上和其它語言的null婉烟、None、nil隅很、NULL一樣率碾,都指代零值或空值,在golang中芬膝,nil只能賦值給指針、channel仔粥、func、interface谭羔、map或slice類型的變量麦向。如果未遵循這個規(guī)則,則會引發(fā)panic.
在底層诵竭,interface作為兩個成員來實現(xiàn),一個類型和一個值.接下來通過編寫測試代碼來看看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))
}
我們已經(jīng)知道接口類型變量底層是作為兩個成員來實現(xiàn)裳朋,一個是type病线,一個是data鲤嫡。type用于存儲變量的動態(tài)類型,data用于存儲變量的具體數(shù)據(jù)让虐。在上面的例子中罢荡,第一條打印語句輸出的是:int32对扶。這是因為已經(jīng)顯示的將類型為int64的數(shù)據(jù)1賦值給了interface類型的變量val惭缰,所以val的底層結構應該是:(int32, 1)。我們暫且用這種二元組的方式來描述漱受,二元組的第一個成員為type,第二個成員為data昂羡。第二條打印語句輸出的是:int。這是因為字面量的整數(shù)在golang中默認的類型是int怨愤,所以這個時候val的底層結構就變成了:(int, 1)
在底層蛹批,interface作為兩個成員實現(xiàn),一個類型腐芍,一個值。只有在內部值和類型都設置為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持有無效的數(shù)據(jù)康栈。但它是有類型的(interface{})。所以val的底層結構應該是:(interface{}, nil)啥么。很顯然,無論該指針的值是什么:(interface{}, nil)菠秒,這樣的接口值總是非nil的氯迂,即使在該指針的內部為nil。