golang基础笔记(11):泛型
泛型
在Go 1.18版本中,添加了对泛型的支持。泛型是一种独立于所使用的特定类型的编写代码的方法。使用泛型可以编写出适用于一组类型中的任何一种的函数和类型,便利了代码的编写。
泛型的作用
假设我们有一个调换int参数的函数,现在我们需要一个调换float参数的函数,没有泛型时,我们需要将相同的逻辑、不同的类型参数的函数重复多遍。
1 | func excInt(a, b int) (int, int) { |
但那是有了泛型,就可以很方便的编写出适用所有元素类型的“普适版”的函数
1 | func exchange[T any](a, b T) (T, T) { |
泛型语法
泛型为Go语言添加了三个新的重要特性:
- 函数和类型的类型参数。
- 将接口类型定义为类型集,包括没有方法的类型。
- 类型推断,它允许在调用函数时在许多情况下省略类型参数。
这里我们着重讲类型参数
类型参数
在上面的例子中,我们提到可以使用泛型“很方便的编写出适用所有元素类型的“普适版”的函数”,这就是泛型的第一个作用——类型参数。
Go语言中的函数和类型支持添加类型参数。类型参数列表看起来像普通的参数列表,只不过它使用方括号([]
)而不是圆括号(()
)。
类型形参是作为可选类型,而在对函数实例化时,我们需要指定一个在可选类型范围内的类型实参。
类型实例化
在上面min
函数中,同时支持了int和float64两种类型
1 | m1 := min[int](1, 2) |
向函数(min)提供类型实参(int、float64)被称之为实例化。在成功实例化后,我们会得到一个非泛型的函数,这个函数可以跟其他函数一样被正常调用
1 | fmin := min[float64] // 类型实例化,编译器生成T=float64的min函数 |
类型参数的使用
除了可以在函数中使用泛型,在类型中也可以使用类型参数列表
1 | //对切片封装一层 |
以上的类型都可以被称之为泛型类型。
泛型类型可以有方法,例如可以为上面的Tree
添加一个寻找的方法
1 | func (t *Tree[T])LookUp(x T) *Tree[T] { |
使用泛型类型必须先进行实例化
1 | var stringTree Tree[string] |
类型约束
类似参数列表中每个参数都有对应的参数类型,类型参数列表中的每一个参数都有对应的类型约束。
在Go语言中,实现类型约束的是接口类型。在上面的例子中,我们对于类型约束都省略了一个外层interface{}
,这是通常的写法,但实际上类型约束是一个接口类型
1 | // 类型约束字面量,通常外层interface{}可省略 |
作为类型约束的接口类型可以事先定义且支持复用
1 | // 事先定义好的类型约束类型 |