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 复制代码
结构体字段完全相同
相关推荐
liliangcsdn几秒前
IC计算-前向/远期收益计算和代码示例
开发语言·python·pandas
大模型搬砖人3 分钟前
8. 向量化:用 Python 构建你的第一个向量库
后端
小卿噢7 分钟前
两个线程改互不相关的变量,性能掉了 7 倍
后端
小溪学编程16 分钟前
Java ListIterator 接口详解:双向遍历与列表修改的利器
java·开发语言·windows
影视飓风TIM16 分钟前
C++11 新特性:Lambda表达式、std::function与std::bind
开发语言·c++
程序员清风19 分钟前
为什么不推荐走Agent开发?
java·后端·面试
sbjdhjd21 分钟前
在 8 字符、字母数字过滤与 PHP 版本差异下复盘临时文件命令执行链 | (8字符绕过)
android·java·开发语言·安全·web安全·数据挖掘·php
统计学小王子1 小时前
数学建模国赛倒计时 1 天 ——《统计模型精讲(逐步回归 R 语言实战篇)》
开发语言·数学建模·r语言
Rain的Java大神之路1 小时前
高并发场景下MySQL数据库性能优化实战
java·后端·mysql
明月_清风1 小时前
分治、贪心、回溯、动态规划:四种经典算法思想
后端·算法