go-interface
介绍
本文介绍的是1.17.10版本的interface相关的源码实现部分。
不同于int, struct等具体类型,接口类型是一种抽象类型,它只是定义了规则,是一种约定。通过接口类型的变量或对象,仅仅知道其能提供哪些方法而已。
我们需要了解interface
的内部结构,才能理解这个题目的含义。
interface在使用的过程中,共有两种表现形式
一种为**空接口(empty interface)**,定义如下:
1 | var MyInterface interface{} |
另一种为非空接口(non-empty interface), 定义如下:
1 | type MyInterface interface { |
空interface类型
相对于非空interface,空interface比较简单,它因为没有约定任何函数,所以任何对象都满足这个约定,都可以转换成空interface。先看看是定义,比较简单:
1 | // empty interface |
_type属性:是GO语言中所有类型的公共描述,Go语言几乎所有的数据结构都可以抽象成 _type,是所有类型的公共描述,type负责决定data应该如何解释和操作,type的结构代码如下:
1 | type _type struct { |
data属性: 表示指向具体的实例数据的指针,他是一个unsafe.Pointer
类型,相当于一个C的万能指针void*
。
因为是空的约定,所以相比较于非空interface iface中,关于interface的数据结构都没了,保存实现约定的函数集func也不需要了,只保留了具体类型的type和data
非空interface类型
iface 表示 non-empty interface 的数据结构,非空接口初始化的过程就是初始化一个iface类型的结构.
下面代码中的IPeople即是非空接口类型,而People因为有GetName 和 GetAge这两个函数,即是说People实现了IPeople。
1 | type IPeople interface { |
下面是非空interface数据结构相关的源码,对应上面的应用代码。
1 | type iface struct { |
通过上面的代码和图片可以看到,interface在源码角度上,也是个struct,只不过能因为它是一种约定,一种抽象,所以源码设计上,作者通过iface这个结构来表示非空interface类型的对象,itab(很重要)把原interface类型(IPeople)和具体类型(IPeople)的”元数据”都附带上了,其中包含了原interface类型定义的函数集合(约定),和具体类型实现的函数集合(实现了约定),即约定和实现了约定。当然,在编译或者runtime创建iface/itab的时候,会根据interface定义函数规则,查找具体类型的方法集,当任何一个函数没有实现的话,约定即未实现,转换失败。
题目分析
通过题目,复习interface的内部结构:
- 非空interface类型题目
1 | type People interface { |
结果
1 | BBBBBBB |
解析
People拥有一个Show方法的,属于非空接口,People的内部定义应该是一个iface
结构体,stu是一个指向nil的空指针,但是最后return stu
会触发匿名变量 People = stu
值拷贝动作,所以最后live()
返回给上层的是一个People insterface{}
类型,也就是一个iface struct{}
类型。 stu为nil,只是iface
中的data 为nil而已。 但是iface struct{}
本身并不为nil。
- 空interface类型题目
1 | func Foo(x interface{}) { |
结果
1 | non-empty interface |
分析
不难看出,Foo()
的形参x interface{}
是一个空接口类型eface struct{}
。
当执行Foo(p)
时,指针p复制拷贝给空interface类型的x,而x的结构如下:
1 | type eface struct { // |
所以x结构体本身不为nil,而是data指针指向的p为nil。
- inteface{}与*interface{}
ABCD中哪一行存在错误?
1 | type S struct { |
结果
1 | B、D两行错误 |
分析
因为Golang是强类型语言,interface是所有golang类型的父类 函数中func f(x interface{})
的interface{}
可以支持传入golang的任何类型,包括指针,但是函数func g(x *interface{})
只能接受*interface{}