深入了解Go的interface{}底层原理实现
作者:树獭叔叔 发布时间:2024-05-21 10:19:03
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


猜你喜欢
- .游标方式 1 DECLARE @Data NVARCHAR(max) SET @Data='1,tanw;2,keen
- 1,打开cmd安装PyQt5pip install pyqt52,PyQt5不再提供Qt Designer等工具,所以需要再安装pyqt5-
- ADO也提供更有效率方法来取得数据。GetRows 方法传回一个二维的数组变量,每一行对应Recordset中的一笔记录,且每
- 1.用户输入月份,判断这个月是哪个季节month = int(input('Month:'))if month in [3,
- 现在公布方法:替换editor.js 函数 // Toolbar button onmouseup
- 删除字符串最后一个字符的方法1.使用strip()方法删除最后一个字符Python strip() 方法用于移除字符串头尾指定的字符(默认为
- 项目介绍最近学习django,通过文件上传下载这个小项目,总结下常用的知识点。做这个案例我有以下需求:1.要支持一次上传多个文件2.支持上传
- 导语贪吃蛇,大家应该都玩过。当初第一次接触贪吃蛇的时候 ,还是我爸的数字手机,考试成绩比较好,就会得到一些小奖励,玩手机游戏肯定也在其中首位
- 一 ,做好安装前的清理工作rpm -pa | grep mysql 或者 rpm -qa | grep -i mysqlyum remove
- 1、linux系统一般自带perl可运行程序在:/usr/bin/perl2、perl测试程序#!/usr/bin/perl -wuse w
- 每种语言都有自己的独到之处,或奇特的语法,或不常见的函数,或非标准的执行方式。因此,不论新丁还是老手,看着某个特性会突然醉了。文中总结了10
- 导语泡泡王国 欢乐多多咕噜噜,吹泡泡,七彩泡泡满天飘。大的好像彩气球,小的就像紫葡萄。当泡泡漫天飞舞时,大朋友、小朋友都会情不自禁地被它吸引
- 引言周末我和小明又开始了疯狂的考证学习,昨晚通过合法的手段获取了一套学习资料,却遇到了一个问题:一套完整的资料,被机构拆分成了162个wor
- 本文实例讲述了Python实现FTP上传文件或文件夹实例。分享给大家供大家参考。具体如下:import sys import os impo
- Java 正则表达式正则表达式定义了字符串的模式。正则表达式可以用来搜索、编辑或处理文本。正则表达式并不仅限于某一种语言,但是在每种语言中有
- 前言在python基础知识中有说过,字典是可变的数据类型,其参数又是键对值。setdefault()方法和字典的get()方法在一些地方比较
- 最近学习go语言写了个 成都房地产薪酬 网站,抓取网上的招聘信息并进行统计。中间遇到一些坑在这里记录下来方便以后查阅gzip压缩是每个web
- ASP 组件 FILE对象当前,基于浏览器/服务器模式的应用比较流行。当用户需要将文件传输到服务器上时,常用方法之一是运行FTP服务器并将每
- vue-i18n 仓库地址:https://github.com/kazupon/vue-i18n兼容性:支持 Vue.js 2.x 以上版
- 本文实例为大家分享了python pygame实现五子棋双人联机的具体代码,供大家参考,具体内容如下同一局域网内,服务端开启时,另一机器将I