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)

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Vamei实验室

Java基础10 接口的继承与抽象类

在实施接口中,我们利用interface语法,将interface从类定义中独立出来,构成一个主体。interface为类提供了接口规范。 在继承中,我们为了提...

19810
来自专栏C语言及其他语言

[每日一题]整数调换

自定义函数和数组的应用 题目描述 输入10个整数,将其中最小的数与第一个数对换,把最大的数与最后一个数对换。写三个函数; ①输入10个数;②进行处理;③输出1...

2926
来自专栏逸鹏说道

我为NET狂官方面试题-基础篇

最近帮人过一遍C#基础,出了点题目,有需要的同志拿走 答案不唯一,官方答案只供参考,若有错误欢迎提出~ 答案明天发 面向过程 99乘法表 ? 用循环来输出以...

3279
来自专栏Brian

Scala Turtuial-基本语法

概述 Scala是将面向对象思想与函数式编程思想集一身的编程语言,特别是在大数据和流式处理方面的快速发展,基于Scala语言一些重要的开源框架随之发布,比如:S...

2734
来自专栏怀英的自我修炼

怀英漫谈8 - JS的字符串和数

你好,这周想和你聊聊JS中的基本语法的另一个部分——字符串和数组的使用。这次我们先聊聊字符串和它的方法。

1282
来自专栏Java爬坑系列

【Java入门提高篇】Day14 Java中的泛型初探

  泛型是一个很有意思也很重要的概念,本篇将简单介绍Java中的泛型特性,主要从以下角度讲解:   1.什么是泛型。   2.如何使用泛型。   3.泛型的好处...

3286
来自专栏大数据钻研

让你分分钟学会 javascript 闭包

闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它。...

2764
来自专栏企鹅号快讯

Python入门基础连载(1)数据类型

Python入门很简单,应该说语法还是很简单明了,有一定C或者java或者别的语言基础的亲们都能明白。大数据,机器学习大势所趋,乘着这风,大家一起学习Pytho...

1996
来自专栏坚毅的PHP

【python学习】文本处理之-translate

2011-09-15 看cookbook 看到很神奇的string.translate 函数 可以剔除字符串中你不需要的串,并可做 maketrans映射 ...

3674
来自专栏数据结构与算法

P3370 【模板】字符串哈希

题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字、大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串。 友情提醒:如果真...

2924

扫码关注云+社区

领取腾讯云代金券