专栏首页Golang语言社区interface引发的事件真相

interface引发的事件真相

流动的水没有形状,漂流的风找不到踪迹,一切代码都了然于心,我们在写代码的时候,总是有一种思维定式陪伴左右,在对事物做判断的时候,往往这种思维定式会往正向或反向做推动作用,在开发的过程中如果不小心忽略,往往就是埋下了陷阱,以下代码是大多数新手会遇到的坑,
package main



import (

        "fmt"

)



type People interface {

        Name() string

}

type Student struct{ name string }



func (stu *Student) Name() string {

        return stu.name

}



func getPeople() People {

        var stu *Student

        return stu

}



func main() {

        if getPeople() == nil {

                fmt.Println("AAAAA")

        } else {

                fmt.Println("BBBBB")

        }

}

上面的代码输出什么那?有些人会认为打印AAAAA,因为他们会认为getPeople方法里面stu是nil 所以返回的就是nil,这样想就大错特错,因为虽然返回的stu是nil 但是函数返回时People接口的结构的本身并不是nil,在我们不了解interface内部结构之前请往下看。

为什么我会选择去写一个关于interface的文章那,我认为他在go语言里面有这非常重要的地位,仅次于goroutine和channel的地位,我在未接触go之前一直从事于c#的开发,接口对我来说就是不同组件之间的契约,对这个契约强制你必须去继承接口,而go语言的设计就非常轻巧,只要实现了接口所要求的所有函数即可,go中的接口分为两种一种是空的接口类似这样:

  1. var in interface{}

例外一种是非空的接口即在接口内部声明了一些方法:

type People interface {

        Name() string

}

接下来我就根据上面的例子来对比一下空接口和非空接口内部结构

type eface struct {          //空接口

        _type *_type         //类型信息

        data  unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)

}

type iface struct {          //带有方法的接口

        tab  *itab           //存储type信息还有结构实现方法的集合

        data unsafe.Pointer  //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)

}

eface包含一个类型信息,可以为reflect提供帮助

type _type struct {

        size       uintptr  //类型大小

        ptrdata    uintptr  //前缀持有所有指针的内存大小

        hash       uint32   //数据hash值

        tflag      tflag    

        align      uint8    //对齐

        fieldalign uint8    //嵌入结构体时的对齐

        kind       uint8    //kind 有些枚举值kind等于0是无效的

        alg        *typeAlg //函数指针数组,类型实现的所有方法

        gcdata    *byte

        str       nameOff

        ptrToThis typeOff

}

iface比eface 中间多了一层itab结构

type itab struct {

        inter  *interfacetype  //接口类型

        _type  *_type          //结构类型

        link   *itab           

        bad    int32

        inhash int32      

        fun    [1]uintptr      //可变大小 方法集合

}

itab 存储_type信息和[]fun方法集,从上面的结构我们就可得出,因为data指向了nil 并不代表interface 是nil,所以返回值并不为空,这里的fun(方法集)定义了接口的接收规则,在编译的过程中需要验证是否实现接口,接口的具体细节你可以阅读Go Data Structures: Interfaces

接下来是第二个例子:

package main



import (

        "fmt"

)



type People interface {

        Speak(string) string

}



type Stduent struct{}



func (stu *Stduent) Speak(think string) (talk string) {

        if think == "bitch" {

                talk = "You are a good boy"

        } else {

                talk = "hi"

        }

        return

}



func main() {

        var peo People = Stduent{}

        think := "bitch"

        fmt.Println(peo.Speak(think))

}

上面的代码是不能编译过去的,会提示没有实现该接口,只要我们把var peo People = Stduent{}修改为var peo People = &Stduent{}就可以了,为什么会有这种限制,

这是因为接口定义不规定实现者是否应该使用指针接收还是值接收实现接口。当使用接口时,不能保证底层类型是值还是指针。我们上面的例子中,我们定义了指针接受方法,修改为值接受方法:

func (stu Stduent) Speak(think string) (talk string) {

        if think == "bitch" {

                talk = "You are a good boy"

        } else {

                talk = "hi"

        }

        return

}

我们再次运行打印:

  1. You are a good boy

通过上面测试我们得出一个结论使用值传递方法,接口赋值使用var peo People = Stduent{}或者var peo People = &Stduent{},如果使用指针作为参数传递,则只能使用var peo People = &Stduent{},正是由于interface的灵活性,可以使用golang实现多态的特性,所以我们更要对interface多做深入了解。(本文未来可能会做一些细微的调整)

本文分享自微信公众号 - Golang语言社区(Golangweb)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-06-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go语言中反射的正确使用

    介绍 反射是元数据编程的一种形式,指的是程序获得本身结构的一种能力。不同语言的反射模型实现不一样,本文中的反射,仅仅指的是Go语言中的反射模型。 反射有两个问题...

    李海彬
  • Go语言中反射的正确使用

    介绍 反射是元数据编程的一种形式,指的是程序获得本身结构的一种能力。不同语言的反射模型实现不一样,本文中的反射,仅仅指的是Go语言中的反射模型。 反射有两个问题...

    李海彬
  • Go语言中反射的正确使用

    介绍 反射是元数据编程的一种形式,指的是程序获得本身结构的一种能力。不同语言的反射模型实现不一样,本文中的反射,仅仅指的是Go语言中的反射模型。 反射有两个问题...

    李海彬
  • go中的接口与多态

    本来想格式化下盘,谁知道格式化完以后啥都没有,连系统都没了,几乎和 rm -rf / 有得一拼!

    机智的程序员小熊
  • JAVA实例化泛型

    DH镔
  • Json Schema 快速入门

    Json schema 本身遵循Json规范,本身就是一个Json字符串,先来看一个例子

    软测小生
  • dotnet检测类型是否为泛型

    private static string GetTableName(Type type) { //检测类型是否为泛型...

    hbbliyong
  • oracle中的数组(第一篇)(r4笔记第9天)

    数组在各种编程语言中都是很重要的数据结构实现,在oracle中也有自己的一席之地。自己简单做了几个实验,发现很多东西还是眼高手低,真实去做的时候,里面还是有不少...

    jeanron100
  • 一个SAP CRM WebClient UI component元数据的解析工具

    Jerry Wang
  • SILVA、GREENGENES、RDP三大数据库的序列探索统计

    最近对16s的三大数据库的序列的具体序列情况挺好奇的,决定统计一下各个序列的长度分布情况,以及这些序列具体分布在哪几个V区,有助于我解决后面16So数据的问题。...

    用户1075469

扫码关注云+社区

领取腾讯云代金券