一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

golang: 詳解interface和nil

 quasiceo 2014-02-01

摘要 golang的nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。nil是預(yù)先說明的標識符,也即通常意義上的關(guān)鍵字。在golang中,nil只能賦值給指針、channel、func、interface、map或slice類型的變量。如果未遵循這個規(guī)則,則會引發(fā)panic。對此官方文檔有明確的說明:http://pkg./pkg/builtin/#Type

golang的nil在概念上和其它語言的null、None、nil、NULL一樣,都指代零值或空值。nil是預(yù)先說明的標識符,也即通常意義上的關(guān)鍵字。在golang中,nil只能賦值給指針、channel、func、interface、map或slice類型的變量。如果未遵循這個規(guī)則,則會引發(fā)panic。對此官方有明確的說明:http://pkg./pkg/builtin/#Type

golang中的interface類似于java的interface、PHP的interface或C++的純虛基類。接口就是一個協(xié)議,規(guī)定了一組成員。這個沒什么好說的,本文不打算對宏觀上的接口概念和基于接口的范式編程做剖析。golang語言的接口有其獨到之處:只要類型T的公開方法完全滿足接口I的要求,就可以把類型T的對象用在需要接口I的地方。這種做法的學(xué)名叫做Structural Typing,有人也把它看作是一種靜態(tài)的Duck Typing。所謂類型T的公開方法完全滿足接口I的要求,也即是類型T實現(xiàn)了接口I所規(guī)定的一組成員。

在底層,interface作為兩個成員來實現(xiàn),一個類型和一個值。對此官方也有文檔說明:http:///doc/go_faq.html#nil_error,如果您不習慣看英文,這里有一篇柴大的翻譯:Go中error類型的nil值和nil 。

接下來通過編寫測試代碼和gdb來看看interface倒底是什么。會用到反射,如果您不太了解golang的反射是什么,這里有刑星翻譯自官方博客的一篇文章:反射的規(guī)則,原文在:laws-of-reflection。

$GOPATH/src

----interface_test

--------main.go

main.go的代碼如下:

01package main
02 
03import (
04    "fmt"
05    "reflect"
06)
07 
08func main() {
09    var val interface{} = int64(58)
10    fmt.Println(reflect.TypeOf(val))
11    val = 50
12    fmt.Println(reflect.TypeOf(val))
13}

我們已經(jīng)知道接口類型的變量底層是作為兩個成員來實現(xiàn),一個是type,一個是data。type用于存儲變量的動態(tài)類型,data用于存儲變量的具體數(shù)據(jù)。在上面的例子中,第一條打印語句輸出的是:int64。這是因為已經(jīng)顯示的將類型為int64的數(shù)據(jù)58賦值給了interface類型的變量val,所以val的底層結(jié)構(gòu)應(yīng)該是:(int64, 58)。我們暫且用這種二元組的方式來描述,二元組的第一個成員為type,第二個成員為data。第二條打印語句輸出的是:int。這是因為字面量的整數(shù)在golang中默認的類型是int,所以這個時候val的底層結(jié)構(gòu)就變成了:(int, 50)。借助于gdb很容易觀察到這點:

1cd $GOPATH/src/interface_test
2$ go build -gcflags "-N -l"
3$ gdb interface_test

接下來說說interface類型的值和nil的比較問題。這是個比較經(jīng)典的問題,也算是golang的一個坑。

                                                                                                                ---來自柴大的翻譯

接著來看代碼:

01package main
02 
03import (
04    "fmt"
05)
06 
07func main() {
08    var val interface{} = nil
09    if val == nil {
10        fmt.Println("val is nil")
11    else {
12        fmt.Println("val is not nil")
13    }
14}

變量val是interface類型,它的底層結(jié)構(gòu)必然是(type, data)。由于nil是untyped(無類型),而又將nil賦值給了變量val,所以val實際上存儲的是(nil, nil)。因此很容易就知道val和nil的相等比較是為true的。

1cd $GOPATH/src/interface_test
2$ go build
3$ ./interface_test
4val is nil

對于將任何其它有意義的值類型賦值給val,都導(dǎo)致val持有一個有效的類型和數(shù)據(jù)。也就是說變量val的底層結(jié)構(gòu)肯定不為(nil, nil),因此它和nil的相等比較總是為false。

上面的討論都是在圍繞值類型來進行的。在繼續(xù)討論之前,讓我們來看一種特例:(*interface{})(nil)。將nil轉(zhuǎn)成interface類型的指針,其實得到的結(jié)果僅僅是空接口類型指針并且它指向無效的地址。注意是空接口類型指針而不是空指針,這兩者的區(qū)別蠻大的,學(xué)過C的童鞋都知道空指針是什么概念。

關(guān)于(*interface{})(nil)還有一些要注意的地方。這里僅僅是拿(*interface{})(nil)來舉例,對于(*int)(nil)、(*byte)(nil)等等來說是一樣的。上面的代碼定義了接口指針類型變量val,它指向無效的地址(0x0),因此val持有無效的數(shù)據(jù)。但它是有類型的(*interface{})。所以val的底層結(jié)構(gòu)應(yīng)該是:(*interface{}, nil)。有時候您會看到(*interface{})(nil)的應(yīng)用,比如var ptrIface = (*interface{})(nil),如果您接下來將ptrIface指向其它類型的指針,將通不過編譯。或者您這樣賦值:*ptrIface = 123,那樣的話編譯是通過了,但在運行時還是會panic的,這是因為ptrIface指向的是無效的內(nèi)存地址。其實聲明類似ptrIface這樣的變量,是因為使用者只是關(guān)心指針的類型,而忽略它存儲的值是什么。還是以例子來說明:

01package main
02 
03import (
04    "fmt"
05)
06 
07func main() {
08    var val interface{} = (*interface{})(nil)
09    // val = (*int)(nil)
10    if val == nil {
11        fmt.Println("val is nil")
12    else {
13        fmt.Println("val is not nil")
14    }
15}

很顯然,無論該指針的值是什么:(*interface{}, nil),這樣的接口值總是非nil的,即使在該指針的內(nèi)部為nil。

1cd $GOPATH/src/interface_test
2$ go build
3$ ./interface_test
4val is not nil

 interface類型的變量和nil的相等比較出現(xiàn)最多的地方應(yīng)該是error接口類型的值與nil的比較。有時候您想自定義一個返回錯誤的函數(shù)來做這個事,可能會寫出以下代碼:

01package main
02 
03import (
04    "fmt"
05)
06 
07type data struct{}
08 
09func (this *data) Error() string { return "" }
10 
11func test() error {
12    var p *data = nil
13    return p
14}
15 
16func main() {
17    var e error = test()
18    if e == nil {
19        fmt.Println("e is nil")
20    else {
21        fmt.Println("e is not nil")
22    }
23}

但是很可惜,以上代碼是有問題的。

1cd $GOPATH/src/interface_test
2$ go build
3$ ./interface_test
4e is not nil

我們可以來分析一下。error是一個接口類型,test方法中返回的指針p雖然數(shù)據(jù)是nil,但是由于它被返回成包裝的error類型,也即它是有類型的。所以它的底層結(jié)構(gòu)應(yīng)該是(*data, nil),很明顯它是非nil的。

可以打印觀察下底層結(jié)構(gòu)數(shù)據(jù):

01package main
02 
03import (
04    "fmt"
05    "unsafe"
06)
07 
08type data struct{}
09 
10func (this *data) Error() string { return "" }
11 
12func test() error {
13    var p *data = nil
14    return p
15}
16 
17func main() {
18    var e error = test()
19 
20    d := (*struct {
21        itab uintptr
22        data uintptr
23    })(unsafe.Pointer(&e))
24 
25    fmt.Println(d)
26}
1cd $GOPATH/src/interface_test
2$ go build
3$ ./interface_test
4&{3078907912 0}

正確的做法應(yīng)該是:

01package main
02 
03import (
04    "fmt"
05)
06 
07type data struct{}
08 
09func (this *data) Error() string { return "" }
10 
11func bad() bool {
12    return true
13}
14 
15func test() error {
16    var p *data = nil
17    if bad() {
18        return p
19    }
20    return nil
21}
22 
23func main() {
24    var e error = test()
25    if e == nil {
26        fmt.Println("e is nil")
27    else {
28        fmt.Println("e is not nil")
29    }
30}

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    丰满人妻一二区二区三区av| 91一区国产中文字幕| 久久热在线免费视频精品| 欧美日本道一区二区三区| 国产高清三级视频在线观看| 国产乱人伦精品一区二区三区四区| 国产一区二区三区丝袜不卡| 亚洲一区二区三区国产| 91熟女大屁股偷偷对白| 区一区二区三中文字幕| 日韩精品视频香蕉视频| 黑丝袜美女老师的小逼逼| 在线视频免费看你懂的| 国产欧美日韩在线精品一二区| 成年女人午夜在线视频 | 精品人妻一区二区三区免费| 国产日产欧美精品大秀| 超薄丝袜足一区二区三区| 内射精子视频欧美一区二区| 在线中文字幕亚洲欧美一区| 久久99夜色精品噜噜亚洲av| 日本大学生精油按摩在线观看| 黑人巨大精品欧美一区二区区| 亚洲一区二区三区在线免费| 亚洲一区二区三区熟女少妇| 欧美一区二区三区十区| 欧美成人久久久免费播放| 欧美国产日韩在线综合| 亚洲国产精品久久精品成人| 中文字幕精品少妇人妻| 国产欧美一区二区色综合| 欧美日韩亚洲精品内裤| 中字幕一区二区三区久久蜜桃| 国产一区欧美午夜福利| 国产精品色热综合在线| 免费福利午夜在线观看| 欧美日韩综合综合久久久| 亚洲日本中文字幕视频在线观看| 91插插插外国一区二区婷婷| 亚洲精品成人午夜久久| 成人精品网一区二区三区|