介绍
这篇博文基于我们在 GopherCon 2021 上的演讲:
Go 1.18 版本增加了对泛型的支持。泛型是自第一个开源版本以来我们对 Go 所做的最大改变。在本文中,我们将介绍新的语言功能。我们不会试图涵盖所有细节,但我们会触及所有要点。有关更详细和更长的描述,包括许多示例,请参阅提案文档。有关语言更改的更准确描述,请参阅 更新的语言规范。(请注意,实际的 1.18 实施对提案文件允许的内容施加了一些限制;规范应该是准确的。未来的版本可能会取消一些限制。)
泛型是一种编写代码的方式,它独立于所使用的特定类型。现在可以编写函数和类型以使用一组类型中的任何一种。
泛型为语言添加了三个新的重要内容:
函数和类型的类型参数。
将接口类型定义为类型集,包括没有方法的类型。
类型推断,允许在调用函数时在许多情况下省略类型参数。
类型参数
现在允许函数和类型具有类型参数。类型参数列表看起来像一个普通的参数列表(除了它使用方括号而不是圆括号)。
Min 函数
为了展示泛型是如何工作的,让我们从浮点值的基本非泛型函数开始:
func Min(x, y float64) float64 {
if x < y {
return x
}
return y
}
我们可以通过添加类型参数列表来使这个函数通用,其实就是使其适用于不同的类型。在这个示例中,我们添加了一个带有单个类型参数的类型参数列表T
,然后用T来替换float64
。
func GMin[T ](x, y T) T {
if x < y {
return x
}
return y
}
现在可以通过编写类似的调用来使用类型参数调用此函数
x := GMin[int](2, 3)
GMin
在这种情况下,向 提供类型参数int
称为 实例化。实例化分两步进行。首先,编译器在泛型函数或类型中用所有类型参数替换它们各自的类型参数。其次,编译器验证每个类型参数是否满足各自的约束。我们很快就会明白这意味着什么,但是如果第二步失败,实例化就会失败并且程序无效。
成功实例化后,我们有一个可以像任何其他函数一样调用的非泛型函数。例如,在类似的代码中
fmin := GMin[float64]
m := fmin(2.71, 3.14)
实例化GMin[float64]
产生了我们原来的浮点Min
函数,我们可以在函数调用中使用它。
类型参数也可以与类型一起使用。
type Tree[T interface{}] struct {
left, right *Tree[T]
value T
}
func (t *Tree[T]) Lookup(x T) *Tree[T] { ... }
var stringTree Tree[string]
这里泛型类型Tree
存储类型参数