網站首頁 編程語言 正文
1. interface{}初探
Go是強類型語言,各個實例變量的類型信息正是存放在interface{}中的,Go中的反射也與其底層結構有關。
iface
和 eface
都是 Go 中描述interface{}的底層結構體,區別在于 iface
描述的接口包含方法,而 eface
則是不包含任何方法的空接口:interface{}
。
接下來,我們將詳細剖析iface
和 eface
的底層數據結構。
2. eface
eface
比較簡單,只維護了 _type
字段,表示空接口所承載的具體的實體類型,以及data
描述了具體的值。
type eface struct { _type *_type data unsafe.Pointer }
data
字段是iface
和 eface
都有的結構,這個是一個內存指針,指向interface{}實例對象信息的存儲地址,在這里,我們可以獲取對象的具體屬性的數值信息。
而interface{}的類型信息是存放在_type
結構體中的,如下所示,在eface
中,直接存放了_type
的指針,iface
中多了一層封裝,本節我們主要針對eface
做梳理,所以介紹_type
結構體。
type _type struct { // 類型大小 size uintptr ptrdata uintptr // 類型的 hash 值 hash uint32 // 類型的 flag,和反射相關 tflag tflag // 內存對齊相關 align uint8 fieldalign uint8 // 類型的編號,有bool, slice, struct 等等等等 kind uint8 alg *typeAlg // gc 相關 gcdata *byte str nameOff ptrToThis typeOff }
我們可以看到size
,ptrdata
等表示interface{}對象的類型信息,hash
是其對應的哈希值,用于map等的哈希算法,tflag
與反射相關,而align
與fieldalign
是用來內存對齊的,這與Go底層的內存管理機制有關,Go的內存管理機制類似于Linux中的伙伴系統,是以固定大小的內存塊進行內存分配的,與這個大小進行對齊消除外碎片,提高內存利用率。另外還有一些和gc相關的參數,大家有一個初步的理解與認識就可以了,如果想深入掌握可以專門學習和查看源碼。
3. iface
與eface
不同,iface
結構體中要同時儲存方法信息,其數據結構如下圖所示。正如前面所說的,itab
結構體封裝了_type
結構體,同樣利用_type
儲存類型信息,另外,其還有一些其他的屬性。hash
是對_type
結構體中hash
的拷貝,提高類型斷言的效率。bad
與inhash
都是標記位,提高gc以及其他活動的效率。fun
指向方法信息的具體地址。
另外,interfacetype
,他描述的是接口靜態類型信息。
fun
字段放置和接口方法對應的具體數據類型的方法地址,實現接口調用方法的動態分派,一般在每次給接口賦值發生轉換時會更新此表,或者直接拿緩存的 itab。這里只會列出實體類型和接口相關的方法,實體類型的其他方法并不會出現在這里。如果你學過 C++ 的話,這里可以類比虛函數的概念,至于靜態函數,并不存放在這里。
C++ 和 Go 在定義接口方式上的不同,也導致了底層實現上的不同。C++ 通過虛函數表來實現基類調用派生類的函數;而 Go 通過 itab
中的 fun
字段來實現接口變量調用實體類型的函數。C++ 中的虛函數表是在編譯期生成的;而 Go 的 itab
中的 fun
字段是在運行期間動態生成的。原因在于,Go 中實體類型可能會無意中實現 N 多接口,很多接口并不是本來需要的,所以不能為類型實現的所有接口都生成一個 itab
, 這也是“非侵入式”帶來的影響;這在 C++ 中是不存在的,因為派生需要顯示聲明它繼承自哪個基類。
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 } type interfacetype struct { typ _type pkgpath name mhdr []imethod }
綜合上面的分析,我們可以梳理出,iface
對應的幾個重要數據結構的關系如下圖所示。
4. 接口轉化
通過前面提到的 iface
的源碼可以看到,實際上它包含接口的類型 interfacetype
和 實體類型的類型 _type
,這兩者都是 iface
的字段 itab
的成員。也就是說生成一個 itab
同時需要接口的類型和實體的類型。
->itable
當判定一種類型是否滿足某個接口時,Go 使用類型的方法集和接口所需要的方法集進行匹配,如果類型的方法集完全包含接口的方法集,則可認為該類型實現了該接口。
例如某類型有 m
個方法,某接口有 n
個方法,則很容易知道這種判定的時間復雜度為 O(mn)
,Go 會對方法集的函數按照函數名的字典序進行排序,所以實際的時間復雜度為 O(m+n)
。
Go的接口實現是非侵入式的,而是鴨子模式:如果某個東西長得像鴨子,像鴨子一樣游泳,像鴨子一樣嘎嘎叫,那它就可以被看成是一只鴨子。
因此,只要我們實現了接口對應的方法,也就實現了對應的接口,不需要單獨申明。
原文鏈接:https://juejin.cn/post/7105423957565636639
相關推薦
- 2022-01-22 Redis學習之旅--與SpringBoot的結合
- 2023-01-17 python中終止協程和異常處理方式_python
- 2022-02-07 SSH遠程連接Linux服務器,提示“Keyboard-interactive authentica
- 2022-07-09 python連接clickhouse數據庫的兩種方式小結_python
- 2023-07-07 React中useState的setState方法請求了好多次
- 2022-04-18 Python?字典(Dictionary)詳細介紹_python
- 2022-11-30 ASP.NET?MVC實現本地化和全球化_實用技巧
- 2022-05-14 jQuery選擇器用法介紹_jquery
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支