【Golang】图解Interface

 被动态值、动态类型整的晕头转向?还不快来认识下接口~

01

空接口interface{}

空接口类型可以接收任意类型的数据,它只要记录这个数据在哪儿,是什么类型的就足够了。空接口变量数据结构如下,其中_type指向接口的动态类型元数据,data就指向接口的动态值。
type eface struct {    _type *_type    data  unsafe.Pointer}
就像下面这个例子,为e赋值以前,其中存储的_type和data都为nil。
var e interface{}

图:e赋值前

f, _ := os.Open('eggo.txt')e = f
如果我们把*os.File类型的变量f赋给e。那么变量e的结构如下图所示。
图:赋值后变量e的结构
因为f本身就是个指针,所以e这里的data就等于f,动态类型就是*os.File。值得强调的是类型元数据这里是可以找到类型关联的方法元数据列表的,这一点对于理解“类型断言”至关重要。

02

非空接口

非空接口就是有方法列表的接口类型,一个变量要想赋值给一个非空接口类型,其类型必须要实现该接口要求的所有方法才行。
type iface struct {    tab   *itab    data  unsafe.Pointer}
iface.data记录的是接口的动态值,所以接口要求的方法列表以及与data对应的动态类型信息一定存在itab里面。
type itab struct {    inter  *interfacetype    _type  *_type    hash   uint32    _      [4]byte    fun    [1]uintptr }
-  itab.inter是interface的类型元数据,它里面记录了这个接口类型的描述信息,接口要求的方法列表就记录在interfacetype.mhdr这里。
type interfacetype struct {    typ      _type    pkgpath  name    mhdr     []imethod}   
-  itab._type就是接口的动态类型,也就是被赋给接口类型的那个变量的类型元数据。
  -  itab.hash是从itab._type中拷贝来的,是类型的哈希值,用于快速判断类型是否相等时使用。
  -  itab.fun记录的是动态类型实现的那些接口要求的方法的地址,是从方法元数据中拷贝来的,为的是快速定位到方法。如果itab._type对应的类型没有实现这个接口,则itab.fun[0]=0,这在类型断言时会用到。

03

非空接口赋值前后

如果我们声明一个io.ReadWriter类型的变量rw。被赋值以前,rw的data为nil,tab也为nil。
var rw io.ReadWriter
图:rw赋值前
下面我们把一个*os.File类型的变量f,赋值给rw。
f, _ := os.Open('eggo.txt')rw = f
此时rw的动态值就是f,动态类型就是*os.File。而itab.fun这个数组里记录的是*os.File实现的Read、Write方法的地址。
图:rw赋值后
下面我们再声明一个io.Writer类型的变量w,并把f赋值给w。
var w io.Writer = f
此时w的动态值和动态类型与rw相同,只是二者的接口类型元数据不同,要求的方法列表也不同罢了。
图:w赋值后

04

itab缓存

关于itab我们还要额外关注一点,既然一个非空接口类型和一个动态类型就可以确定一个itab的内容,那这个itab结构体自然是可以被接口类型与动态类型均相同的接口变量复用的。
图:itab结构体可复用
实际上Go语言会把用到的itab结构体缓存起来,并且以<接口类型, 动态类型>组合为key,以*itab为value,构造一个哈希表,用于存储与查询itab信息。
这个哈希表与map底层的哈希表不同,其结构设计更为简便。
type itabTableType struct {    size    uintptr             // length of entries array. Always a power of 2.    count   uintptr             // current number of filled entries.    entries [itabInitSize]*itab // really [size] large}
需要一个itab时,会首先去itabTable里查找,计算哈希值时会用到接口类型(itab.inter)和动态类型(itab._type)的类型哈希值:
func itabHashFunc(inter *interfacetype, typ *_type) uintptr { return uintptr(inter.typ.hash ^ typ.hash)}
如果能查询到对应的itab指针,就直接拿来使用。若没有就要再创建,然后添加到itabTable中。
了解了空接口和非空接口的数据结构,明确了接口动态值与动态类型在赋值前与赋值后的变化,接下来就可以看看类型断言”是怎么回事儿了。
(0)

相关推荐

  • 一文吃透 Go 语言解密之接口 interface

    大家好,我是煎鱼. 自古流传着一个传言...在 Go 语言面试的时候必有人会问接口(interface)的实现原理.这又是为什么?为何对接口如此执着? 实际上,Go 语言的接口设计在整体扮演着非常重要 ...

  • C开源hash代码uthash的用法总结

    uthash 是C的比较优秀的开源代码,它实现了常见的hash操作函数,例如查找.插入.删除等待.该套开源代码采用宏的方式实现hash函数的相关功能,支持C语言的任意数据结构最为key值,甚至可以采用 ...

  • 【新提醒】圆育克束腰上衣 图解

    【新提醒】圆育克束腰上衣 图解

  • 电机的装配工艺图解

    专业的电工电气领域自媒体,不容错过 电机制造是整个机械制造业中一个重要部门,电机的主要作用是产生驱动转矩,作为用电器或各种机械的动力源.电机制造有其特有的工艺. 一.装配前准备.检查及要求 1.清理与 ...

  • 老牛常用低吸战法图解

    深山老牛 楼主 2019-03-17 12:56 我之前讲的战法比较凌乱,大家都一知半解,可能不利于形成总体印象而建立自我系统.我也意识到了.老牛在这里综合性的图解一次.为避免交易同质化,不详解,我相 ...

  • 如何判断筹码结构的好坏(图解)(2)

    再讲重点,筹码的纯洁性. 这个比较难理解,我只能通过举例来说. 做短线或者超短打板,参与牛股的人主要是谁?激进的操作者,他们的操作是最极端的,买按涨停买,卖按跌停卖.那么反应到盘面就是,极端的走势. ...

  • 如何判断筹码结构的好坏(图解)

    简单来说,好的短线筹码结构,就是两个: 1.筹码断层,筹码断层使得实际流通盘极少,拉升和打压都非常方便. 2.筹码的纯洁性,指的是只有极少的获利筹码或者风险偏好低的猥琐筹码的存在,这两个是牛股炒作的重 ...

  • 绳子打结方法大全(图解)

    <渔业界>:中国渔业最大新媒体 结绳系扣是渔民的最基本技能.渔业生产中,无论是网眼的补织.断绳的对接,还是船靠码头时的系缆,都要用到绳索结.绳子扣产生于渔家的生产.生活中的需求,他们要综合 ...

  • 解决不良?看这个图解8D分析法。直观明了!

    8D(8 Disciplines)即问题解决8步法,最早是福特公司使用的经典质量问题分析手法,对于解决工厂中存在的问题是一个很有用的工具,尤其在面对重大不良时,它能建立一个体系,让整个团队共享信息,并 ...