加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-16 18:25:40 所属栏目:大数据 来源:网络整理
导读:本文转自发布在csdn的博客,原文:http://blog.csdn.net/qq_26981997/article/details/52608081,欢迎指正。 对go做过开发的朋友都很熟悉interface。这几天在网上看到了篇文章,谈到了interface与nil判等的问题。题是好题,就进一步了解了一下。 原题如下:
                                                                                                     

本文转自发布在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.kind=%5v,cat2 == nil,reflect.TypeOf(cat2).Kind(),reflect.ValueOf(cat2)) //接口变量,type!=nil,所以cat2!==nil

fmt.Printf("cat3 information: nil?:%5v,cat3 == nil,reflect.TypeOf(cat3),reflect.TypeOf(cat3).Kind(),reflect.ValueOf(cat3)) //接口变量,type!=nil,所以cat3!=nil

fmt.Printf("cat4 information: nil?:%5v,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,value=&{}

//cat4 information: nil?:false,type= *main.Gafield,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,value=%v,%v /n",reflect.ValueOf(cat1),d1) //接口变量,type、value都是nil,所以cat1==nil

fmt.Printf("cat2 information: nil?:%5v,type.kind=%10v,reflect.ValueOf(cat2),d2) //接口变量,type!=nil,所以cat2!==nil

fmt.Printf("cat3 information: nil?:%5v,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,value=<nil>,&{7024192 0}

//cat2 information: nil?:false,type.kind= ptr,value=&{},&{7024192 7302976}

//cat3 information: nil?:false,&{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/

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读