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

alex_023 · · 2111 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

本文转自我发布在csdn的博客,原文:http://blog.csdn.net/qq_26981997/article/details/52608081, 欢迎指正。 对go做过开发的朋友都很熟悉interface。这几天在网上看到了篇文章,谈到了interface与nil判等的问题。题是好题,就进一步了解了一下。 ### 原题如下: **Nil接口并不是有Nil指针的接口** ```Go 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进行了描述:[原文][1] - interface的内部实现,其实有两个很核心的元素,那就是**type**与**value**。 - interface==nil,仅当type、value均为nil,即(nil,nil)。很多时候,type有值,而value==nil,比如上题。 - 实际开发中,不应存在type==nil,value!=nil的情况。 因此,原题的解答应该是:为type确定了类型指针,但value依然没有赋值。 ### 更多的疑问? 查看了一些资料,有几个困惑,需要逐个分析: - 接口变量是否为指针类型? - 结构体指针能否与其接口变量判等? 首先定义一个全局的接口和对应的两个实现类,便于后续的分析。 ```Go //接口 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来了解基础结构类别。 ```Go var ( cat2 = GetNilTabbyCat() ) fmt.Printf("cat1 information: type=%15v,kind=%10v \n",reflect.TypeOf(cat2),reflect.TypeOf(cat2).Kind()) ``` 通过结果,我们可以知道,**cat2是指针**. **接口变量之间的判等** ```Go 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变量的类别和值的情况,代码如下: ```Go 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} ``` **接口变量能否与其结构体指针判等** 从前面代码对比可以知道,接口变量是指针。那接口指针是否会与结构体指针相同呢? ```Go 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. 接口指针与其对应实现的结构体指针,**可以**进行判等操作。 [1]: http://golang.org/doc/faq#nil_error

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2111 次点击  ∙  2 赞  
加入收藏 微博
2 回复  |  直到 2016-10-14 11:35:30
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传