2009 年 Go 语言首次发布后,支持泛型一直以来呼声最高的功能之一。十年磨一剑!Go 官方终于在 2022 年 03 月 15 日发布 go1.18 stable 正式支持泛型
什么是泛型
泛型的核心是把类型参数化
,通俗的来说就是允许使用时才指定类型的一种设计。
假设有个两变量相加的需求,且这俩变量可能是 int 或者 string,简单粗暴就直接定义两个函数分别支持 int、string
1 | // 传统写法 |
那如果使用泛型来定义呢,它的写法大概是这样的
1 | // 泛型写法 |
好处显而易见,至少可以少拷贝一个函数。当然这不足以体现出泛型的优点,毕竟这种通用函数通过interface
一样可以实现。
1 | func Add(var1, var2 interface{}) interface{} { |
既然通过interface
也可以实现,那么为什么泛型还备受推崇呢?
- 便利性 & 效率
使用 interface 实现则需要使用断言或者反射来处理类型问题,同时 Go 语言中反射性能十分低下(见下图)。
- 安全性
interface 极易引发类型异常问题,编译器在编译过程中不能发现类型问题。

Go 的泛型
在 Go 语言中如何使用泛型,先了解一下泛型的 3 个概念:
- 泛型参数
- 泛型参数类型约束
- 泛型参数列表
Go 泛型的代码风格和其他语言基本类似,比如定义一个只支持 int 和 string 两值相加的泛型函数:
1 | func Add[T int | string](var1, var2 T) T { |
以上函数,
T 泛型参数,可以是任意字母且不区分大小写(但通常使用大写字母)。
int | string 泛型参数类型约束,用于声明和约束泛型参数 T 支持的数据类型。比如
1 | Add[T float64] // 只支持float64类型 |
- [] 中括号内是泛型参数列表,支持任意个泛型参数,使用逗号分隔。
1 | Add[T int64, T2 float64](var1, var2 T, var3 T2) |
如果泛型仅仅能在函数上使用好像有点“鸡肋”。它还可以这样去使用,
1 | // 泛型结构体 |
以上只是简单列举,还支持一些列“套娃”行为。值的注意的是,Go 新增的操作符~
。
1 | type MyType interface { |
使用操作符~,意味不仅支持该操作符后的基本类型,同时还支持任意底层类型是该类型的数据。
以上内容参考来源:https://tip.golang.org/doc/go1.18
什么时候使用泛型
当你要为不同数据类型做着同样的处理逻辑时,可以考虑使用泛型。泛型是把双刃剑,既可以提高编码效率、提升代码美学,同样也可以让代码可读性变的很差。