【Go基础】interface
接口 interface
推荐阅读:【Golang】图解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
}
注:类型元数据这里可以找到类型关联的方法元数据列表。

非空接口
非空接口就是有方法列表的接口类型。如果一个变量想赋值一个非空接口类型,那么其类型必须实现该接口的要求的所有方法才行。非空接口类型如下:
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.File。itab.fun 这个数组记录的是 *os.File 这个类型实现的 io.ReadWriter 接口的方法。

注:rw 能使用的方法只是接口类型已经注册的。
总结:
接口,你可以看作是一个工具(枪),你往里面装怎么样的子弹,他就会打出什么样子弹。
itab缓存
一个非空接口类型 interfacetype 和一个动态类型 _type 就可以确定一个 itab了。剩余的字段要么来自 _type 要么是动态类型的方法。似乎这个 itab 是可以复用的。那么对于两个个非空接口类型定义的变量,赋值后只要动态类型不变,变得只是动态值 data。
实际上 Go 语言会把用到的 itab 结构体缓存起来,并且以 <接口类型,动态类型> 组合为 key,以 *itab 为 value,构成一个哈希表,用于存储与查询以及复用 itab 信息

这个哈希表可与我们使用的 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