Go-接口实现原理
接口的底层结构
eface
和iface
eface
和iface
都是描述接口的数据结构,区别在于iface
描述的接口包含方法、而eface
描述的接口不包含方法
eface
的数据结构
type eface struct {
_type *_type //实体类型
data unsafe.Pointer //具体的值
}
eface
主要包含实体类型_type
指针和指向具体值的指针data
,
-
_type
描述了实体类型包括内存大小、对齐方式等等,所有类型均可解释为_type
-
data
存储了数据的指针,即使是对于数字、字符串这样的字面值也是通过额外分配空间取指针的
iface
的数据结构
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod
}

在iface
中存储的是一个itab
指针和一个指向数据的指针,itab
中:
-
_type
依旧描述了实体类型包括内存大小、对齐方式等数据 -
inter
则描述了接口自身的信息,其中的mhdr
字段包含了接口所定义的方法 -
fun
其实是一个动态数组,虽然声明时是固定大小为1,但在使用时会直接通过fun
指针获取其中的数据,类似于c
语言中的不定长结构体
值接收者和指针接受者
package main
type Person struct {
age int
}
func (p Person) howOld() int {
return p.age
}
func (p *Person) growUp() {
p.age += 1
}
func main() {
p1 := Person{}
p2 := &Person{}
p1.howOld()
p1.growUp()
p2.howOld()
p2.growUp()
}
howOld
是值接收者、growUp
是指针接者,对于普通的函数调用而言在使用者go
内部做了兼容可以互相调用
但是在接口中这是不兼容的,指针类型、值类型赋值给接口其生成的函数集是不一样的
函数集
值类型 | 函数集 |
---|---|
值(T)
|
(t T) |
指针(*T)
|
(t T) 和 (t *T)
|
原因很简单:对于有一些值是无法取到指针的,比如数字、字符串的字面值
网友评论