likes
comments
collection
share

【Go基础】interface

作者站长头像
站长
· 阅读数 17

接口 interface

推荐阅读:【Golang】图解Interface

深入研究 Go interface 底层实现

空接口 interface{}

空接口类型可以接受任意类型的数据。干的事情不多,记录数据的位置和数据类型即可。空接口类型如下:

type eface struct {
    _type *_type //指向接口的动态类型元数据
    data  unsafe.Pointer //指向接口的动态值。
}

举个例子🌰:

var e interface{} //这里定了一个空接口类型

//在 e 没有赋值前
type eface struct {
    _type *_type //指向接口的动态类型元数据 ->nil
    data  unsafe.Pointer //指向接口的动态值。 ->nil
}
f, _ := os.Open("eggo.txt")
e = f

如果将 *os.File 类型的变量 f 赋值给 e。来看看变量 e 的结构。

type eface struct {
    _type *_type //指向接口的动态类型元数据 ->*os.File 的类型元数据
    data  unsafe.Pointer //指向接口的动态值。 ->f
}

注:类型元数据这里可以找到类型关联的方法元数据列表。

【Go基础】interface

非空接口

非空接口就是有方法列表的接口类型。如果一个变量想赋值一个非空接口类型,那么其类型必须实现该接口的要求的所有方法才行。非空接口类型如下:

type iface struct {
    tab   *itab
    data  unsafe.Pointer //接口的动态值
}
//itab
type itab struct {
    inter  *interfacetype //接口的类型元数据
    _type  *_type //指向接口的动态类型元数据
    hash   uint32 //itab._type中拷贝来的,类型哈希值,用于快速判断类型是否相等时使用,后续会有介绍
    _      [4]byte
    fun    [1]uintptr //动态类型实现的接口要求方法地址
}
//interfacetype
type interfacetype struct {
    typ      _type
    pkgpath  name
    mhdr     []imethod //接口要求的方法列表
}   

举个例子🌰:

我们声明了一个 io.ReadWriter 的接口类型变量 rw。被赋值前,结构如下:

type iface struct {
    tab   *itab  //->nil
    data  unsafe.Pointer //接口的动态值 ->nil
}
f, _ := os.Open("eggo.txt")
rw = f

我们将 *os.File 类型变量 f。赋值给 rw

那么 rw 的动态值是 f,动态类型是 *os.Fileitab.fun 这个数组记录的是 *os.File 这个类型实现的 io.ReadWriter 接口的方法。

【Go基础】interface

注:rw 能使用的方法只是接口类型已经注册的。

总结:

接口,你可以看作是一个工具(枪),你往里面装怎么样的子弹,他就会打出什么样子弹。

itab缓存

一个非空接口类型 interfacetype 和一个动态类型 _type 就可以确定一个 itab了。剩余的字段要么来自 _type 要么是动态类型的方法。似乎这个 itab 是可以复用的。那么对于两个个非空接口类型定义的变量,赋值后只要动态类型不变,变得只是动态值 data

实际上 Go 语言会把用到的 itab 结构体缓存起来,并且以 <接口类型,动态类型> 组合为 key,以 *itabvalue,构成一个哈希表,用于存储与查询以及复用 itab 信息

【Go基础】interface

这个哈希表可与我们使用的 map 底层的哈希表不同,结构设计更为简便。

type itabTableType struct {
    size    uintptr             // length of entries array. Always a power of 2. // entries 数组的长度。总是 2 的幂。
    count   uintptr             // current number of filled entries.  //当前填充的entries数目
    entries [itabInitSize]*itab // really [size] large //实际的大小
}
  • 当需要一个 itab 会先去 itabTable 里面查找,计算哈希值时会用到接口类型(itab.inter)和动态类型(itab._type)的类型哈希值
  • 如果能查询到对应的 itab 指针就直接拿来用,如果没有就直接创建一个新的 itab
转载自:https://juejin.cn/post/7038173052227551245
评论
请登录