Go interface 底层的 itab 到底是什么

你已经知道:

go 复制代码
type Animal interface {
    Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
    return "汪汪"
}

这里:

txt 复制代码
Animal = 接口
Dog = 结构体
Speak = 方法

那么:

Go 到底是怎么知道:

go 复制代码
var a Animal = Dog{}

里面的 Dog 实现了 Animal?

又是怎么找到:

go 复制代码
a.Speak()

应该调用哪个函数的?

答案:

就是:

txt 复制代码
itab

一、先看这段代码

go 复制代码
type Animal interface {
	Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
	return "汪汪"
}

func main() {

	var a Animal

	a = Dog{}

	fmt.Println(a.Speak())
}

输出:

txt 复制代码
汪汪

二、问题来了

变量:

go 复制代码
a

类型是:

go 复制代码
Animal

但是:

真正装进去的是:

go 复制代码
Dog{}

那么:

执行:

go 复制代码
a.Speak()

的时候:

Go 怎么知道:

应该去调用:

go 复制代码
Dog.Speak()

而不是:

go 复制代码
Cat.Speak()

呢?


三、interface 底层结构

前面讲过:

interface 本质:

txt 复制代码
(Type, Value)

例如:

go 复制代码
var a Animal = Dog{}

底层:

大概是:

txt 复制代码
T = Dog
V = Dog{}

四、但仅仅有类型还不够

因为:

Go 还需要知道:

txt 复制代码
这个类型实现了哪些接口方法

例如:

txt 复制代码
Dog
↓
Speak()
↓
对应哪个函数地址

五、于是出现了 itab

itab:

全称:

txt 复制代码
interface table

翻译:

txt 复制代码
接口方法表

六、itab 是干什么的

一句话:

txt 复制代码
记录
某个具体类型
如何实现某个接口

七、画图理解

例如:

go 复制代码
var a Animal = Dog{}

底层:

大概这样:

txt 复制代码
a
│
├── itab
│
└── data

八、data

data:

保存:

txt 复制代码
真正的数据

也就是:

txt 复制代码
Dog{}

九、itab

itab:

保存:

txt 复制代码
接口信息
+
具体类型信息
+
方法地址

十、对应到你的代码


接口类型

go 复制代码
type Animal interface {
    Speak() string
}

对应:

txt 复制代码
Animal

具体类型

go 复制代码
type Dog struct{}

对应:

txt 复制代码
Dog

方法实现

go 复制代码
func (d Dog) Speak() string

对应:

txt 复制代码
Dog.Speak()

十一、itab 里面存什么

大概可以理解成:

txt 复制代码
itab
├── interface type
├── concrete type
└── method table

具体:

txt 复制代码
itab
├── Animal
├── Dog
└── Dog.Speak()

十二、画成表

项目 对应
interface type Animal
concrete type Dog
method Dog.Speak

十三、执行 a.Speak() 时发生什么

代码:

go 复制代码
a.Speak()

Go:

先找到:

txt 复制代码
a.itab

然后:

在里面找到:

txt 复制代码
Speak 对应的函数地址

最终调用:

go 复制代码
Dog.Speak()

十四、整个过程

txt 复制代码
a.Speak()
↓
找到 itab
↓
找到 Speak 函数地址
↓
执行 Dog.Speak()
↓
返回 汪汪

十五、为什么接口调用比普通调用慢一点

普通调用:

go 复制代码
dog.Speak()

编译时:

就知道:

txt 复制代码
调用哪个函数

接口调用:

go 复制代码
a.Speak()

需要:

txt 复制代码
先查 itab
再跳转函数

所以:

会多一步。


十六、再举一个例子

go 复制代码
type Cat struct{}

func (c Cat) Speak() string {
	return "喵喵"
}

此时:

go 复制代码
var a Animal = Cat{}

底层:

变成:

txt 复制代码
itab
├── Animal
├── Cat
└── Cat.Speak()

十七、为什么不用提前写 implements

Java:

java 复制代码
class Dog implements Animal

Go:

不用。

因为:

Go 编译器:

会自动检查:

txt 复制代码
Dog 有没有实现 Animal 的全部方法

如果有:

自动生成:

txt 复制代码
Animal-Dog 的 itab

十八、编译器什么时候创建 itab

当你写:

go 复制代码
var a Animal = Dog{}

时。


编译器发现:

txt 复制代码
Dog 实现了 Animal

于是:

创建:

txt 复制代码
Animal ↔ Dog

对应的 itab。


十九、为什么接口断言能成功

例如:

go 复制代码
var a Animal = Dog{}

执行:

go 复制代码
dog := a.(Dog)

Go:

会看:

txt 复制代码
itab 里的 concrete type

发现:

txt 复制代码
Dog

于是:

断言成功。


二十、结合 interface = (Type, Value) 再理解

很多教程说:

txt 复制代码
interface = (Type, Value)

其实:

更准确:

应该理解成:

txt 复制代码
interface
├── itab
└── data

其中:


data

保存:

txt 复制代码
真正的数据

例如:

txt 复制代码
Dog{}

itab

保存:

txt 复制代码
Dog 的类型
Animal 的信息
Dog.Speak 的地址

二十一、最终完整图(重点)

txt 复制代码
var a Animal = Dog{}

底层:

txt 复制代码
a
│
├── itab
│   ├── interface type : Animal
│   ├── concrete type  : Dog
│   └── method table
│       └── Speak → Dog.Speak
│
└── data
    └── Dog{}

二十二、最后一句总结(必须记住)

itab 本质:

txt 复制代码
接口方法映射表

作用:

txt 复制代码
记录
某个具体类型
如何实现某个接口

在你的例子里:

txt 复制代码
Animal  ← 接口类型
Dog     ← 具体类型
Dog.Speak ← 方法实现

Go 会生成:

txt 复制代码
itab
├── Animal
├── Dog
└── Speak → Dog.Speak

当执行:

go 复制代码
a.Speak()

时:

实际上:

就是:

txt 复制代码
通过 itab
找到 Dog.Speak()
再调用

所以:

txt 复制代码
interface 能装不同类型
却还能正确调用对应方法

核心秘密:

就是:

txt 复制代码
itab + data
相关推荐
葫芦和十三4 小时前
图解 MongoDB 04|索引模型:每建一个索引,就是在 B+-tree 森林里多栽一棵
后端·mongodb·agent
用户47949283569156 小时前
claude Fable用不了?把Gpt 5.5pro接到你的claude code里
前端·后端
GetcharZp8 小时前
告别 Nginx 复杂配置!这款带 Web 面板的万能代理神器,让端口转发变得如此简单
后端
IT_陈寒10 小时前
React的useState居然还有这种坑?我差点删库跑路
前端·人工智能·后端
Pedantic11 小时前
SwiftUI 手势笔记
前端·后端
金銀銅鐵11 小时前
[Python] 从《千字文》中随机挑选汉字
后端·python
飘尘14 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
浏览器工程师15 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
行者全栈架构师15 小时前
Maven dependency:tree 的 8 个高级用法
java·后端