Go 泛型
本文将介绍 Go 中的泛型。
一、设计发展
2009 年 12 月 3 日,Go 团队的 Russ Cox 发表了一篇名为 The Generic Dilemma 的文章,列举了现有的 3 种泛型实现方案:
- C 的泛型支持:
- 不实现泛型
- 避免引入复杂性
- 影响程序员开发效率
- C++ 的泛型支持:
- 在编译时为每个类型生成一份单独的函数函数实现
- 将产生大量的、多余的、重复的代码
- Java 的泛型支持:
- Java 泛型
- 包含隐式的装箱拆箱,影响代码执行效率
自此,Go 团队开始了对 Go 泛型设计的探索,在 12 年的时间中,不断进行着方案的提出、修改、否定。
直至 2021 年 12 月 14 日,Go 1.18 beta1 版本发布,Go 正式支持泛型。
二、泛型语法
1 |
|
其中,
类型参数:
- P、Q、R 为类型参数
- 类型参数列表用
[]
包裹
类型约束:
aConstraint
、interface{~int | ~string}
、~int | string
为类型约束类型约束规定了类型实参必须满足的条件
类型约束通过接口定义
因此,在 Go 1.18 之后,接口不仅可以规定应该实现的方法的集合,也可以规定可传入的泛型实参的类型的集合。
类型约束支持简写,如
interface{~int | ~string}
、~int | string
如果支持多种类型,应该将类型通过
|
联合如果支持的是类型本身,应该为
类型名
;如果支持的是以该类型为底层类型的所有类型,应该为~类型名
三、使用示例
下面这段代码中,
- 定义了泛型约束 Number,限制传入的类型为
int | int32 | float32 | float64
- 定义了泛型方法
sort
- 定义了泛型方法
toString
1 |
|
四、泛型函数实例化
Go 会在编译阶段中对泛型函数进行实例化,具体过程如下:
通过
显示传入 / 自动推断
得到类型实参判断类型实参是否满足类型约束
将泛型函数实例化为具体函数,即生成一份适用于类型实参的普通函数
当使用相同的类型实参对泛型函数进行多次调用时,Go 仅会实例化一次
Go 不会为每种类型实参实例化一个函数,而是以 GC Shape 为单位进行实例化,GC Shape 相同的不同类型实参将会共享同一个函数
GC Shape 指的是类型在 Go 垃圾收集器中的表示,它由类型的大小、所需的对齐方式以及类型中包含指针的部分所决定
因此,在 Go 的泛型方案下,泛型函数不仅能正确生效,并且不会影响运行时性能。