接口与结构体 | 青训营笔记

98 阅读4分钟

这是我参与「第五届青训营」笔记创作活动的第14天

什么是接口 Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

Go的接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让对象更加灵活和更具有适应能力。很多面向对象的语言都有相似的接口概念,但Go语言中接口类型的独特之处在于它是满足隐式实现的鸭子类型。

所谓鸭子类型说的是:只要走起路来像鸭子、叫起来也像鸭子,那么就可以把它当作鸭子。Go语言中的面向对象就是如此,如果一个对象只要看起来像是某种接口类型的实现,那么它就可以作为该接口类型使用。

就比如说在c语言中,使用printf在终端输出的时候只能输出有限类型的几个变量,而在go中可以使用fmt.Printf,实际上是fmt.Fprintf向任意自定义的输出流对象打印,甚至可以打印到网络甚至是压缩文件,同时打印的数据不限于语言内置的基础类型,任意隐士满足fmt.Stringer接口的对象都可以打印,不满足fmt.Stringer接口的依然可以通过反射的技术打印。

结构体类型 interface实际上就是一个结构体,包含两个成员。其中一个成员是指向具体数据的指针,另一个成员中包含了类型信息。空接口和带方法的接口略有不同,下面分别是空接口的数据结构:

struct Eface { Type* type; void* data; }; 1 2 3 4 5 其中的Type指的是:

struct Type { uintptr size; uint32 hash; uint8 _unused; uint8 align; uint8 fieldAlign; uint8 kind; Alg *alg; void *gc; String *string; UncommonType *x; Type *ptrto; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 和带方法的接口使用的数据结构:

struct Iface { Itab* tab; void* data; }; 1 2 3 4 5 其中的Iface指的是:

struct Itab { InterfaceType* inter; Type* type; Itab* link; int32 bad; int32 unused; void (*fun[])(void); // 方法表 }; 1 2 3 4 5 6 7 8 9 3.9.3 具体类型向接口类型赋值 将一个具体类型数据赋值给interface这样的抽象类型,需要进行类型转换。这个转换过程中涉及哪些操作呢?

如果转换为空接口,返回一个Eface,将Eface中的data指针指向原型数据,type指针会指向数据的Type结构体。

如果将其转化为带方法的interface,需要进行一次检测,该类型必须实现interface中声明的所有方法才可以进行转换,这个检测将会在编译过程中进行。检测过程具体实现式通过比较具体类型的方法表和接口类型的方法表来进行的。

具体类型方法表:Type的UncommonType中有一个方法表,某个具体类型实现的所有方法都会被收集到这张表中。 接口类型方法表:Iface的Itab的InterfaceType中也有一张方法表,这张方法表中是接口所声明的方法。Iface中的Itab的func域也是一张方法表,这张表中的每一项就是一个函数指针,也就是只有实现没有声明。 这两处方法表都是排序过的,只需要一遍顺序扫描进行比较,应该可以知道Type中否实现了接口中声明的所有方法。最后还会将Type方法表中的函数指针,拷贝到Itab的fun字段中。Iface中的Itab的func域也是一张方法表,这张表中的每一项就是一个函数指针,也就是只有实现没有声明。

3.9.4 获取接口类型数据的具体类型信息 接口类型转换为具体类型(也就是反射,reflect),也涉及到了类型转换。reflect包中的TypeOf和ValueOf函数来得到接口变量的Type和Value。