golang積累-介面指標與結構體指標

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

對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的內部實現,其實有兩個很核心的元素,那就是typevalue
  • 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())

通過結果,我們可以知道,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=          <nil>, value=<invalid reflect.Value>      //cat2 information: nil?:false, type=    *main.Tabby, type.kind=  ptr, value=<nil>      //cat3 information: nil?:false, type=    *main.Tabby, type.kind=  ptr, value=&{}      //cat4 information: nil?:false, type=  *main.Gafield, type.kind=  ptr, value=<nil>      //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=<nil> ,&{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指向的地址不同,但比較的是具體的值,所以相等。

簡單結論:

  1. 指標的判斷,都涉及到type和value。
  2. 介面指標之間的判等,要基於type與value,一個不同則不等。
  3. 介面指標與其對應實現的結構體指標,可以進行判等操作。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.