抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

前言

总所周知,interface是Go中的一个关键字,是一组方法的集合,只定义了方法的形态,同时也是Go中多态的一种实现方式。

1
2
3
4
5
6
type interfaceName interface {

TODO()

// ...some method
}

关于接口的命名,我们通常以er作为后缀,表示某个行为。根据接口中是否有定义方法,运行时Go中分为了两个结构体来代表,分别是ifaceeface

在了解这两个结构体时,我们先来看看一个常见的interface面试题,下面这个例子输出什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type People interface {
Show()
}

type Student struct{}

func (stu *Student) Show() {

}

func live() People {
var stu *Student
return stu
}

func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}

了解interface结构体就知道答案是输出:BBBBBBB。那么下面就来学习下Go对interface做了啥操作把,这两个结构体的源码在runtime/runtime2.go下就可以找到啦

iface

当接口里有定义方法时,就会使用iface这个结构体,它具有两个属性:

1
2
3
4
type iface struct {
tab *itab
data unsafe.Pointer
}
  • tab用来存储接口本身的相关信息,例如:接口的类型信息、方法集信息、具体类型信息以及具体类型信息的方法集
  • data则指向当前具体类型的值
1
2
3
4
5
6
7
8
9
10
11
12
13
type itab struct {
inter *interfacetype // 接口信息
_type *_type // 具体类型的信息
hash uint32 // 类型转换的hash值,用于接口转换为具体类型时
_ [4]byte // 对象对齐
fun [1]uintptr // 一组函数指针
}

type interfacetype struct {
typ _type // 接口类型
pkgpath name // 接口包路径
mhdr []imethod // 接口方法集
}

下面来看个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Sayer interface {
Say(word string)
}

type People struct {
}

func (p *People) Say(word string) {
fmt.Println("People:", word)
}

func main() {
var people *People
var sayer Sayer
fmt.Println(people == nil) // true
fmt.Println(sayer == nil) // true
sayer = people
fmt.Println(sayer == nil) // false
}
  • people是一个引用类型变量,因此它的零值是nil,所以people==nil为true
  • sayer也同样是一个引用类型变量,它的两个属性tabdata都为nil,因此sayer==nil也为true
  • 将people赋值给sayer,这时tab属性会存储相关的具体类型和接口类型,因此tab属性不为nil,data属性被赋值为具体属性people的值,同样是nil。这时在sayer==nil就为false了。

image-20221021154136325

因此只有一个iface接口,tab属性和data属性都为nil时,该接口才为nil。

eface

因为eface只用来表示方法集为空的接口,因此eface就是iface的缩减版。

只使用_type属性指向具体类型信息,data同样指向具体类型变量的值。

1
2
3
4
type eface struct {
_type *_type // 具体类型信息
data unsafe.Pointer // 具体类型变量值
}

image-20221021154437837

这时回头看那个面试题就知道为啥啦,live()方法返回stu变量,同时因为方法返回值为People类型,因此发生了一次隐形赋值,类似如下

1
2
var people People = stu
return people

这时iface结构体的tab属性就不为nil啦,表达式live()==nil为false。

参考资料