Go语言类型系统

Go的类型系统

一、内置类型和自定义类型

  • 常用的整型、浮点型、布尔型均为内置类型。通过type关键字自行定义的类型为自定义类型。
  • Go不允许为内置类型定义方法,也不允许为接口定义方法。

二、类型元数据

  • Go通过类型元数据记录类型相关的信息,源码位置在runtime包的** _type结构体 **。每种类型的类型元数据都是全局唯一的。结构体中的成员变量记录了类型的名称、占用内存大小、对齐、是否为自定义类型等等信息。每个类型结构体都会包含一个_type成员,和一些其他记录类型信息的成员。例如切片类型的结构体中,除了自身的_type成员,还会有一个指向切片内容类型元数据的指针成员。如果是自定义类型,还会有一个uncommontype结构体,记录包的路径、关联方法数、方法元数据等等信息。
  • 上述实现方法导致以下两种写法:
    – type myType int32
    – type myType = int32
    会有一些差别,后者自定义类型和内置类型共享类型元数据,类似int32和rune的关系。前者则会定义自己的类型元数据。

三、接口

空接口

  • 空接口是没有定义任何方法的接口。事实上,空接口与带有方法的自定义接口底层实现并不相同。
  • 空接口的源码位置位于runtime包的eface结构体。这个结构体由一个记录类型元数据的_type指针和一个记录接口动态值的unsafe.pointer指针构成

非空接口

  • 非空接口的结构体由一个itab指针和一个记录接口动态值的unsafe.pointer指针构成。itab结构体包含的成员有:接口本身的类型元数据,因为非空接口具有关联的方法,所以需要类型元数据、方法列表等记录;动态类型的类型元数据;类型哈希值,用于快速判断类型是否相等。为了避免二次查找,itab会从原类型的类型元数据中拷贝方法列表。
  • 由上述实现中,我们很容易联想到,itab的本质是接口和类型的一种对应关系。所以事实上Go会以动态类型和接口的组合为key,itab结构体指针为value构建一张缓存表。源码位于runtime.itabTableType。缓存表是一张哈希表,用接口类型的哈希值和动态类型的哈希值作异或运算的结果为哈希值。

四、类型断言

  • Go的类型断言可以用于空接口或非空接口,断言的类型可以是具体类型或者非空接口类型。断言成功后会返回对应动态类型的返回值,断言失败后会返回对应类型的零值。

空接口断言具体类型

  • 对于空接口,只需要比较接口的_type指针是否指向所要断言的动态类型。

非空接口断言具体类型

  • 因为有接口类型、动态类型和itab指针的缓存表设计,在对非空接口进行具体类型断言时可以直接检查缓存表。如果接口和动态类型对应的itab指针指向对应类型的itab,则断言成功,反之则断言失败。

空接口断言非空接口 & 非空接口断言非空接口

  • 此时,断言的本质是判断空接口装载的动态类型是否实现了非空接口的方法列表。所以此时会先检查缓存表,若未命中缓存,再检查方法列表。值得注意的是,检查缓存表时,即使命中缓存,也必须要检查itab.fun[0]。因为在断言失败后,也会储存一个键值对到缓存表,但是会将itab.fun[0]置0,标志着这种动态类型并未实现此接口的方法,使得失败的断言也可以利用缓存。

五、装箱

  • 从上文中我们知道了,接口的实现大致可以分为接口类型信息和接口动态值。接口动态值的地址存储在接口源代码的unsafe.pointer中。我们不禁想到,在符合条件时,一个接口可以装载任意类型的数据,无论是值类型还是指针类型。对于指针类型很简单,一台机器上所有指针的长度都等于机器字长。但是对于值类型,接口是如何装载的呢?事实上,接口装载的值类型变量会被分配在栈或者堆上,接口中装载其地址。具体分配在栈还是堆上,需要以变量的逃逸分析结果确定。

六、动态派发

  • 动态多态最关键的步骤就是要实现动态派发:“在何时的场合选择调用合适的方法”。以C++举例,同一种指针调用的方法,可以在运行时确定具体的调用版本。C++实现这种效果的设计就是虚函数表。这种机制大家都很熟悉,这里暂不赘述。在Go中,实现动态派发的机制如下:
  • itab结构体中会有指针记录此接口需要实现的方法,_type指针会指向接口中装载变量的动态类型方法集。这两个方法列表都是有序的,所以在O(1)级别的时间开销中即可比较确定对应方法,并且把方法的指针存储到itab的fun成员中。再结合itab缓存,因为每个接口类型和具体类型的组合都有对应的itab缓存,大部分时间在调用方法时,通过itab缓存即可实现动态派发,找到具体调用方法的地址所在。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值