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)
|
原因很簡單:對於有一些值是無法取到指針的,比如數字、字符串的字面值