golang积累-接口指针与结构体指针

来源:互联网 发布:淘宝宝贝降权查询 编辑:程序博客网 时间:2024/04/27 22:13
                                                                                                     

本文转自发布在csdn的博客,原文:http://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。解答是“将一个指针返回了空指针”。说实话,真心没看懂!


官方对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()

}

//实现类1

type 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

}

//实现类2

type 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指向的地址不同,但比较的是具体的值,所以相等。

简单结论:

指针的判断,都涉及到type和value。

接口指针之间的判等,要基于type与value,一个不同则不等。

接口指针与其对应实现的结构体指针,可以进行判等操作。




查看原文:http://www.zoues.com/2016/10/20/golang%e7%a7%af%e7%b4%af-%e6%8e%a5%e5%8f%a3%e6%8c%87%e9%92%88%e4%b8%8e%e7%bb%93%e6%9e%84%e4%bd%93%e6%8c%87%e9%92%88/
0 0