from:https://blog.csdn.net/qq_26981997/article/details/52608081
對go做過開發的朋友都很熟悉interface。這幾天在網上看到了篇文章,談到了interface與nil判等的問題。題是好題,就進一步瞭解了一下。原題如下:Nil介面並不是有Nil指標的介面type Cat interface { Meow()}type Tabby struct {}func (*Tabby) Meow() { fmt.Println("meow") }func GetACat() Cat { var myTabby *Tabby = nil // Oops, we forgot to set myTabby to a real value return myTabby}func TestGetACat(t *testing.T) { if GetACat() == nil { t.Errorf("Forgot to return a real cat!") }} 毫無疑問,輸出結果是空。也就是說GetACat()方法返回的值,不為nil。解答是“將一個指標返回了null 指標”。說實話,真心沒看懂!官方對interface的定義官方在常見問題中,對interface判斷nil進行了描述:原文interface的內部實現,其實有兩個很核心的元素,那就是type與value。interface==nil,僅當type、value均為nil,即(nil,nil)。很多時候,type有值,而value==nil,比如上題。實際開發中,不應存在type==nil,value!=nil的情況。 因此,原題的解答應該是:為type確定了類型指標,但value依然沒有賦值。更多的疑問?查看了一些資料,有幾個困惑,需要逐個分析: - 介面變數是否為指標類型? - 結構體指標能否與其介面變數判等?首先定義一個全域的介面和對應的兩個實作類別,便於後續的分析。//介面type Cat interface { Meow()}//實作類別1type Tabby struct{}func (*Tabby) Meow() { fmt.Println("Tabby meow") }func GetNilTabbyCat() Cat { var myTabby *Tabby = nil return myTabby}func GetTabbyCat() Cat { var myTabby *Tabby = &Tabby{} return myTabby}//實作類別2type Gafield struct{}func (*Gafield) Meow() { fmt.Println("Gafield meow") }func GetNilGafieldCat() Cat { var myGafield *Gafield = nil return myGafield}func GetGafieldCat() Cat { var myGafield *Gafield = &Gafield{} return myGafield} 介面變數是否為指標類型? 在面對類型時,可以利用反射包(reflect)的TypeOf擷取的Type,再調用Kind來瞭解基礎結構類別。 var ( cat2 = GetNilTabbyCat() ) fmt.Printf("cat1 information: type=%15v,kind=%10v \n",reflect.TypeOf(cat2),reflect.TypeOf(cat2).Kind())1234通過結果,我們可以知道,cat2是指標. 介面變數之間的判等 var ( cat1 Cat = nil cat2 = GetNilTabbyCat() cat3 = GetTabbyCat() cat4 = GetNilGafieldCat() ) fmt.Printf("cat1 information: nil?:%5v, type=%15v, value=%5v \n", cat1 == nil, reflect.TypeOf(cat1), reflect.ValueOf(cat1)) //介面變數,type、value都是nil,所以cat1==nil fmt.Printf("cat2 information: nil?:%5v, type=%15v, type.kind=%5v, value=%5v \n", cat2 == nil, reflect.TypeOf(cat2), reflect.TypeOf(cat2).Kind(), reflect.ValueOf(cat2)) //介面變數,type!=nil,所以cat2!==nil fmt.Printf("cat3 information: nil?:%5v, type=%15v, type.kind=%5v, value=%5v \n", cat3 == nil, reflect.TypeOf(cat3), reflect.TypeOf(cat3).Kind(), reflect.ValueOf(cat3)) //介面變數,type!=nil, 所以cat3!=nil fmt.Printf("cat4 information: nil?:%5v, type=%15v, type.kind=%5v, value=%5v \n", cat4 == nil, reflect.TypeOf(cat4), reflect.TypeOf(cat4).Kind(), reflect.ValueOf(cat4)) //介面變數, fmt.Printf("cat1==cat2?%5v , cat2==cat3?%5v, cat2==cat4?%5v \n", cat1 == cat2, cat2 == cat3, cat2 == cat4) //Output: //cat1 information: nil?: true, type=, value=//cat2 information: nil?:false, type= *main.Tabby, type.kind= ptr, value=//cat3 information: nil?:false, type= *main.Tabby, type.kind= ptr, value=&{} //cat4 information: nil?:false, type= *main.Gafield, type.kind= ptr, value=//cat1==cat2?false , cat2==cat3?false, cat2==cat4?false 從運行結果看,介面變數之間判斷,是要比較type和value的。cat1的type是空,所以cat1!=cat2。cat2與cat3的值不同,所以不等。cat2與cat4的type不同,所以不等。 更進一步,其實可以使用unsafe.Pointer來瞭解,可以很清楚的瞭解cat2變數的類別和值的情況,代碼如下:type iface struct { itype uintptr ivalue uintptr }d1 := (*iface)(unsafe.Pointer(&cat1))d2 := (*iface)(unsafe.Pointer(&cat2))d3 := (*iface)(unsafe.Pointer(&cat3))d4 := (*iface)(unsafe.Pointer(&cat4))fmt.Println(d1)fmt.Println(d2)fmt.Println(d3)fmt.Println(d4)//Output://&{0 0} //&{7024192 0} //&{7024192 7302976} //&{7024128 0} 介面變數能否與其結構體指標判等 從前面代碼對比可以知道,介面變數是指標。那介面指標是否會與結構體指標相同呢? type iface struct { itype uintptr ivalue uintptr } var ( cat1 Cat = GetNilTabbyCat() //介面指標 cat2 = GetTabbyCat() //介面指標 cat3 *Tabby = &Tabby{} //結構體指標 ) d1 := (*iface)(unsafe.Pointer(&cat1)) d2 := (*iface)(unsafe.Pointer(&cat2)) d3 := (*iface)(unsafe.Pointer(&cat3)) fmt.Printf("cat1 information: nil?:%5v, type=%15v, value=%v ,%v \n", cat1 == nil, reflect.TypeOf(cat1), reflect.ValueOf(cat1), d1) //介面變數,type、value都是nil,所以cat1==nil fmt.Printf("cat2 information: nil?:%5v, type=%15v, type.kind=%10v, value=%v ,%v \n", cat1 == nil, reflect.TypeOf(cat2), reflect.TypeOf(cat2).Kind(), reflect.ValueOf(cat2), d2) //介面變數,type!=nil,所以cat2!==nil fmt.Printf("cat3 information: nil?:%5v, type=%15v, type.kind=%10v, value=%v ,%v \n", cat3 == nil, reflect.TypeOf(cat3), reflect.TypeOf(cat3).Kind(), reflect.ValueOf(cat3), d3) //介面變數,type!=nil, 所以cat3!=nil fmt.Printf("cat1==cat2?:%5v, cat2==cat3?%v \n", cat1==cat2,cat2==cat3 ) //Output: //cat1 information: nil?:false, type= *main.Tabby, value= ,&{7024192 0}
//cat2 information: nil?:false, type= *main.Tabby, type.kind= ptr, value=&{} ,&{7024192 7302976}
//cat3 information: nil?:false, type= *main.Tabby, type.kind= ptr, value=&{} ,&{7302976 0}
//cat1==cat2?:false, cat2==cat3?true
可以看出,結構體指標是可以與介面指標進行判等的,但要注意,儘管cat2、cat3的ivalue指向的地址不同,但比較的是具體的值,所以相等。
簡單結論:
指標的判斷,都涉及到type和value。
介面指標之間的判等,要基於type與value,一個不同則不等。
介面指標與其對應實現的結構體指標,可以進行判等操作。