GoLang中的iface?和?eface?的區(qū)別解析
GoLang之iface 和 eface 的區(qū)別是什么?
iface和eface都是 Go 中描述接口的底層結(jié)構(gòu)體,區(qū)別在于iface描述的接口包含方法,而eface則是不包含任何方法的空接口:interface{}。
從源碼層面看一下:
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype
_type *_type
link *itab
hash uint32 // copy of _type.hash. Used for type switches.
bad bool // type does not implement interface
inhash bool // has this itab been added to hash?
unused [2]byte
fun [1]uintptr // variable sized
}
iface內(nèi)部維護(hù)兩個指針,tab指向一個itab實體, 它表示接口的類型以及賦給這個接口的實體類型。data則指向接口具體的值,一般而言是一個指向堆內(nèi)存的指針。
再來仔細(xì)看一下 itab 結(jié)構(gòu)體:_type 字段描述了實體的類型,包括內(nèi)存對齊方式,大小等;inter 字段則描述了接口的類型。fun 字段放置和接口方法對應(yīng)的具體數(shù)據(jù)類型的方法地址,實現(xiàn)接口調(diào)用方法的動態(tài)分派,一般在每次給接口賦值發(fā)生轉(zhuǎn)換時會更新此表,或者直接拿緩存的 itab。
這里只會列出實體類型和接口相關(guān)的方法,實體類型的其他方法并不會出現(xiàn)在這里。如果你學(xué)過 C++ 的話,這里可以類比虛函數(shù)的概念。
另外,你可能會覺得奇怪,為什么 fun 數(shù)組的大小為 1,要是接口定義了多個方法可怎么辦?實際上,這里存儲的是第一個方法的函數(shù)指針,如果有更多的方法,在它之后的內(nèi)存空間里繼續(xù)存儲。從匯編角度來看,通過增加地址就能獲取到這些函數(shù)指針,沒什么影響。順便提一句,這些方法是按照函數(shù)名稱的字典序進(jìn)行排列的。
再看一下
interfacetype類型,它描述的是接口的類型:
type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod
}
可以看到,它包裝了
_type類型,_type實際上是描述 Go 語言中各種數(shù)據(jù)類型的結(jié)構(gòu)體。我們注意到,這里還包含一個mhdr字段,表示接口所定義的函數(shù)列表,pkgpath記錄定義了接口的包名。
這里通過一張圖來看下
iface結(jié)構(gòu)體的全貌:

接著來看一下
eface的源碼:
type eface struct {
_type *_type
data unsafe.Pointer
}
相比
iface,eface就比較簡單了。只維護(hù)了一個_type字段,表示空接口所承載的具體的實體類型。data描述了具體的值。

我們來看個例子:
package main
import "fmt"
func main() {
x := 200
var any interface{} = x
fmt.Println(any)
g := Gopher{"Go"}
var c coder = g
fmt.Println(c)
}
type coder interface {
code()
debug()
}
type Gopher struct {
language string
}
func (p Gopher) code() {
fmt.Printf("I am coding %s language\n", p.language)
}
func (p Gopher) debug() {
fmt.Printf("I am debuging %s language\n", p.language)
}
執(zhí)行命令,打印出匯編語言:
go tool compile -S ./src/main.go
可以看到,main 函數(shù)里調(diào)用了兩個函數(shù):
func convT2E64(t *_type, elem unsafe.Pointer) (e eface) func convT2I(tab *itab, elem unsafe.Pointer) (i iface)
上面兩個函數(shù)的參數(shù)和
iface及eface結(jié)構(gòu)體的字段是可以聯(lián)系起來的:兩個函數(shù)都是將參數(shù)組裝一下,形成最終的接口。
作為補充,我們最后再來看下
_type結(jié)構(gòu)體:
type _type struct {
// 類型大小
size uintptr
ptrdata uintptr
// 類型的 hash 值
hash uint32
// 類型的 flag,和反射相關(guān)
tflag tflag
// 內(nèi)存對齊相關(guān)
align uint8
fieldalign uint8
// 類型的編號,有bool, slice, struct 等等等等
kind uint8
alg *typeAlg
// gc 相關(guān)
gcdata *byte
str nameOff
ptrToThis typeOff
}
Go 語言各種數(shù)據(jù)類型都是在
_type字段的基礎(chǔ)上,增加一些額外的字段來進(jìn)行管理的:
type arraytype struct {
typ _type
elem *_type
slice *_type
len uintptr
}
type chantype struct {
typ _type
elem *_type
dir uintptr
}
type slicetype struct {
typ _type
elem *_type
}
type structtype struct {
typ _type
pkgPath name
fields []structfield
}
這些數(shù)據(jù)類型的結(jié)構(gòu)體定義,是反射實現(xiàn)的基礎(chǔ)。
到此這篇關(guān)于GoLang之iface 和 eface 的區(qū)別是什么的文章就介紹到這了,更多相關(guān)GoLang iface 和 eface區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Go語言中關(guān)于包導(dǎo)入必學(xué)的 8 個知識點
這篇文章主要介紹了詳解Go語言中關(guān)于包導(dǎo)入必學(xué)的 8 個知識點,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
詳解Golang time包中的結(jié)構(gòu)體time.Ticker
在日常開發(fā)過程中,會頻繁遇到對時間進(jìn)行操作的場景,使用 Golang 中的 time 包可以很方便地實現(xiàn)對時間的相關(guān)操作,接下來的幾篇文章會詳細(xì)講解 time 包,本文講解一下 time 包中的結(jié)構(gòu)體 time.Ticker,需要的朋友可以參考下2023-08-08
淺析Go中函數(shù)的健壯性,panic異常處理和defer機制
這篇文章主要為大家詳細(xì)介紹了Go中函數(shù)的健壯性,panic異常處理和defer機制的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-10-10
深入理解Golang中的Protocol Buffers及其應(yīng)用
本篇文章將深入探討 Go 語言中使用 Protobuf 的基礎(chǔ)知識、常見應(yīng)用以及最佳實踐,希望能幫大家了解如何在項目中高效利用 Protobuf2024-11-11
Go語言strconv包實現(xiàn)字符串和數(shù)值類型的相互轉(zhuǎn)換
這篇文章主要介紹了Go語言strconv包實現(xiàn)字符串和數(shù)值類型的相互轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03

