首先,我们先了解什么泛型。
在维基百科上解释如下:
泛型程序设计(英文:generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
Go 1.18版本增加了对泛型的支持,泛型也是自 Go 语言开源以来所做的最大改变。
我们来看下在1.18版本以前,我们怎么实现一个反转切片的函数
func reverse(s []int) []int {
l := len(s)
r := make([]int, l)
for i, e := range s {
r[l-i-1] = e
}
return r
}
fmt.Println(reverse([]int{1, 2, 3, 4})) // [4 3 2 1]
可是这个函数只能接收[]int类型的参数,如果我们想支持[]float64类型的参数,我们就需要再定义一个reverseFloat64Slice函数。
func reverseFloat64Slice(s []float64) []float64 {
l := len(s)
r := make([]float64, l)
for i, e := range s {
r[l-i-1] = e
}
return r
}
可以看出,每实现一个数据类型,都需要写相应函数,一遍一遍地编写相同的功能是低效的,实际上这个反转切片的函数并不需要知道切片中元素的类型,但为了适用不同的类型我们把一段代码重复了很多遍。
Go1.18之前我们可以尝试使用反射去解决上述问题,但是使用反射在运行期间获取变量类型会降低代码的执行效率并且失去编译期的类型检查,同时大量的反射代码也会让程序变得晦涩难懂。
类似这样的场景就非常适合使用泛型。从Go1.18开始,使用泛型就能够编写出适用所有元素类型的“普适版”reverse函数。
package main
import "fmt"
func reverseWithGenerics[T any](s []T) []T {
l := len(s)
r := make([]T, l)
for i, e := range s {
r[l-i-1] = e
}
return r
}
func main() {
fmt.Println(reverseWithGenerics([]any{1, 2, 3, 4, "23s1", 212, 111.1})) // [4 3 2 1]
}
//输出结果:[111.1 212 23s1 4 3 2 1]
泛型为Go语言添加了三个新的重要特性:
类型参数
类型形参和类型实参
我们之前已经知道函数定义时可以指定形参,函数调用时需要传入实参。
现在,Go语言中的函数和类型支持添加类型参数。类型参数列表看起来像普通的参数列表,只不过它使用方括号([])而不是圆括号(())。
借助泛型,我们可以声明一个适用于一组类型的min函数
func min[T int | float64](a, b T) T {
if a <= b {
return a
}
return b
}
评论