前言
总所周知,interface是Go中的一个关键字,是一组方法的集合,只定义了方法的形态,同时也是Go中多态的一种实现方式。
1 | type interfaceName interface { |
关于接口的命名,我们通常以er作为后缀,表示某个行为。根据接口中是否有定义方法,运行时Go中分为了两个结构体来代表,分别是iface
和eface
。
在了解这两个结构体时,我们先来看看一个常见的interface面试题,下面这个例子输出什么?
1 | type People interface { |
了解interface结构体就知道答案是输出:BBBBBBB
。那么下面就来学习下Go对interface做了啥操作把,这两个结构体的源码在runtime/runtime2.go
下就可以找到啦
iface
当接口里有定义方法时,就会使用iface
这个结构体,它具有两个属性:
1 | type iface struct { |
- tab用来存储接口本身的相关信息,例如:接口的类型信息、方法集信息、具体类型信息以及具体类型信息的方法集
- data则指向当前具体类型的值
1 | type itab struct { |
下面来看个例子
1 | type Sayer interface { |
- people是一个引用类型变量,因此它的零值是nil,所以
people==nil
为true - sayer也同样是一个引用类型变量,它的两个属性
tab
和data
都为nil,因此sayer==nil
也为true - 将people赋值给sayer,这时tab属性会存储相关的具体类型和接口类型,因此tab属性不为nil,data属性被赋值为具体属性people的值,同样是nil。这时在
sayer==nil
就为false了。
因此只有一个iface
接口,tab
属性和data
属性都为nil时,该接口才为nil。
eface
因为eface
只用来表示方法集为空的接口,因此eface
就是iface
的缩减版。
只使用_type属性指向具体类型信息,data同样指向具体类型变量的值。
1 | type eface struct { |
这时回头看那个面试题就知道为啥啦,live()方法返回stu变量,同时因为方法返回值为People类型,因此发生了一次隐形赋值,类似如下
1 | var people People = stu |
这时iface结构体的tab属性就不为nil啦,表达式live()==nil
为false。