打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
【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中。

了解了空接口和非空接口的数据结构,明确了接口动态值与动态类型在赋值前与赋值后的变化,接下来就可以看看类型断言”是怎么回事儿了。




本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
结构体变量和结构体类型的定义
Go Interface 源码剖析
golang弹出消息框
iOS 读懂runtime基础(一)
golang: 常用数据类型底层结构分析
nginx 源码学习笔记(六)——nginx基本数据结构
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服