
这块:
其实是 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
结构体字段完全相同