Go 里 interface 为什么能比较?到底在比什么?

这块:

其实是 Go 里面非常经典、非常容易懵的知识点。

因为:

很多人看到:

go 复制代码
fmt.Println(stu1 == stu2) // false
fmt.Println(stu3 == stu4) // true

会想:

txt 复制代码
明明内容都是 Tom
为什么一个 false 一个 true?

真正原因:

在于:

txt 复制代码
interface 底层到底存了什么

今天:

我们彻底讲清楚。


一、先看代码

go 复制代码
type Stu struct {
	Name string
}

type StuInt interface{}

func main() {

	var stu1, stu2 StuInt = &Stu{"Tom"}, &Stu{"Tom"}

	var stu3, stu4 StuInt = Stu{"Tom"}, Stu{"Tom"}

	fmt.Println(stu1 == stu2) // false

	fmt.Println(stu3 == stu4) // true
}

二、interface 底层到底是什么(重点)

Go 里的 interface:

底层:

其实包含两个东西:

txt 复制代码
类型(Type)
值(Value)

也就是:

txt 复制代码
interface = (T, V)

三、例如

go 复制代码
var x interface{} = 123

此时:

txt 复制代码
T = int
V = 123

四、再例如

go 复制代码
var x interface{} = "hello"

此时:

txt 复制代码
T = string
V = "hello"

五、所以 interface 本质

其实:

像一个盒子。

盒子里:

装着:

txt 复制代码
值
+
这个值的类型

六、interface 比较规则(必须记住)

两个 interface 相等:

必须同时满足:


1. 类型相同

txt 复制代码
T 相同

2. 值相同

txt 复制代码
V 相同

七、也就是说

txt 复制代码
(T1, V1) == (T2, V2)

必须:

txt 复制代码
T1 == T2
并且
V1 == V2

八、现在分析 stu1 和 stu2(重点)


代码

go 复制代码
var stu1, stu2 StuInt = &Stu{"Tom"}, &Stu{"Tom"}

九、这里放进去的是什么

注意:

你写的是:

go 复制代码
&Stu{"Tom"}

十、& 是什么

表示:

txt 复制代码
取地址

得到:

txt 复制代码
指针

十一、所以 interface 里装的是


stu1

txt 复制代码
T = *Stu
V = 0x1000

stu2

txt 复制代码
T = *Stu
V = 0x2000

十二、为什么地址不同

因为:

go 复制代码
&Stu{"Tom"}

每次:

都会:

txt 复制代码
创建新的结构体对象

内存地址:

自然不同。


十三、真正底层发生了什么

第一次:

go 复制代码
&Stu{"Tom"}

可能:

在内存:

txt 复制代码
0x1000

第二次:

go 复制代码
&Stu{"Tom"}

可能:

在:

txt 复制代码
0x2000

十四、虽然内容一样

txt 复制代码
Name 都是 Tom

但是:

txt 复制代码
指针地址不同

十五、所以比较的是

txt 复制代码
0x1000 == 0x2000

结果:

txt 复制代码
false

十六、真正核心(重点)

指针比较:

比较的是:

txt 复制代码
地址

不是:

txt 复制代码
内容

十七、所以

go 复制代码
fmt.Println(stu1 == stu2)

结果:

txt 复制代码
false

十八、现在分析 stu3 和 stu4(重点)


代码

go 复制代码
var stu3, stu4 StuInt = Stu{"Tom"}, Stu{"Tom"}

十九、这里和前面最大区别

注意:

这里:

txt 复制代码
没有 &

所以:

放进去的:

不是指针。

而是:

txt 复制代码
结构体值本身

二十、所以 interface 里装的是


stu3

txt 复制代码
T = Stu
V = Stu{Name:"Tom"}

stu4

txt 复制代码
T = Stu
V = Stu{Name:"Tom"}

二十一、现在比较什么

比较:

txt 复制代码
结构体内容

二十二、Go 结构体如何比较

Go:

会逐字段比较。


Name

txt 复制代码
Tom == Tom

成立。


二十三、所以结果

txt 复制代码
true

二十四、真正核心区别(必须理解)


指针 interface

txt 复制代码
比较地址

结构体 interface

txt 复制代码
比较字段内容

二十五、画图理解(重点)


stu1

txt 复制代码
interface
├── T = *Stu
└── V = 0x1000

stu2

txt 复制代码
interface
├── T = *Stu
└── V = 0x2000

比较

txt 复制代码
0x1000 != 0x2000

所以:

txt 复制代码
false

二十六、再看 stu3

txt 复制代码
interface
├── T = Stu
└── V = {Name:"Tom"}

stu4

txt 复制代码
interface
├── T = Stu
└── V = {Name:"Tom"}

二十七、比较

txt 复制代码
字段完全相同

所以:

txt 复制代码
true

二十八、为什么 interface 能直接 ==

因为:

interface:

底层:

知道:

txt 复制代码
类型
+
值

所以:

Go:

可以比较。


二十九、但是有坑(重点)

不是:

所有 interface 都能比较。


三十、例如 slice

go 复制代码
var a interface{} = []int{1,2}
var b interface{} = []int{1,2}

fmt.Println(a == b)

三十一、会直接 panic

因为:

txt 复制代码
slice 不可比较

三十二、为什么 slice 不可比较

因为:

slice:

底层:

包含:

txt 复制代码
指针
长度
容量

Go:

不允许:

直接 ==。


三十三、哪些类型可以比较


可以

  • int
  • string
  • bool
  • 指针
  • 数组
  • 结构体(字段都可比较)

不可以

  • slice
  • map
  • func

三十四、所以 interface 比较还有个规则

除了:

txt 复制代码
T 相同
V 相同

还必须:

txt 复制代码
V 本身是可比较类型

三十五、最经典 nil 坑(超级重要)

例如:

go 复制代码
var p *Stu = nil

var x interface{} = p

三十六、此时 x 是 nil 吗

很多新人:

会以为:

txt 复制代码
是 nil

其实:

txt 复制代码
不是

三十七、为什么

因为:

interface 里:

其实是:

txt 复制代码
T = *Stu
V = nil

三十八、真正 nil interface

必须:

txt 复制代码
T = nil
V = nil

三十九、所以

go 复制代码
fmt.Println(x == nil)

结果:

txt 复制代码
false

四十、这个是 Go 面试超级经典题

因为:

很多人:

不理解:

txt 复制代码
interface = (T, V)

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

interface 底层:

本质:

txt 复制代码
(Type, Value)

两个 interface 相等:

必须:

txt 复制代码
类型相同
+
值相同

指针比较

txt 复制代码
比较地址

结构体比较

txt 复制代码
比较字段

所以:

go 复制代码
&Stu{"Tom"} != &Stu{"Tom"}

因为:

txt 复制代码
两个不同地址

而:

go 复制代码
Stu{"Tom"} == Stu{"Tom"}

因为:

txt 复制代码
结构体字段完全相同
相关推荐
ping某29 分钟前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy34 分钟前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom43 分钟前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
用户1474853079745 小时前
CodeX使用Skill生成游戏美术和音乐资源,一分钟入门
后端
Melody1235 小时前
用 abort 中断 AI 流式请求,我之前做错了
后端
onething3656 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 5 —— SSE 流式输出 + 打字机效果
人工智能·后端·全栈
一个做软件开发的牛马6 小时前
MyBatis-Plus 从零实战:完整搭建可运行 Demo,BaseMapper 零 SQL、Wrapper 条件构造、分页插件与代码生成器详解
java·后端
码事漫谈6 小时前
AI 编程的「三体」架构:OpenSpec + Superpowers + GStack 如何让一个开发者撑起整个研发团队
后端
吃饱了得干活6 小时前
深入解析 OpenFeign:从重试、拦截到负载均衡的全维度实践
后端
onething3656 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 6 —— 业务完善 + 会话消息预览
人工智能·后端·全栈