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
相关推荐
candyTong1 小时前
Claude Code 每次调用 API 时,上下文是怎么"拼"出来的?
javascript·后端·架构
java_cj1 小时前
MySQL 执行原理深度剖析:查询成本计算与优化器内幕
数据库·后端·mysql
java_cj1 小时前
数据库范式化设计与性能优化全攻略
数据库·后端·性能优化·架构·开源
千纸鹤の脉搏1 小时前
多线程的初步了解---进程与线程
java·开发语言·学习·线程
秋田君2 小时前
Qt 5.12.8 下载与安装教程(附网盘资源)
开发语言·qt
雪隐2 小时前
AI股票小助手01-量化交易基础概念
人工智能·后端·python
故事和你912 小时前
洛谷-【动态规划2】线性状态动态规划4
开发语言·数据结构·c++·算法·动态规划·图论
alwaysrun2 小时前
Rust之代数数据类型Enum
后端·rust·编程语言
前端市界2 小时前
拒绝纸上谈兵!Docker 一键全线打通 DevOps 金三角实战
后端