泛型的出现
泛型是Go语言在1.18版本中才引入的新特性,这是许多开发者期盼已久的一个功能。
所谓泛型,就是在定义函数、接口或数据类型时,不指定具体的类型,而是使用类型占位符(比如 T 或者 any),在实际使用时再指定具体的类型。
简单来说,泛型就是一种在编程时定义函数或数据类型的一种方法,允许它们具有多种数据类型。因此,使用泛型可以更具灵活性地编写代码。
package mAIn
import "fmt"
//定义一个数据类型 T
type T interface{}
//定义一个泛型函数,该函数接收一个类型为 T 的列表和一个函数,然后返回一个新的列表
func Map(t T, f func(T) T) []T {
nt := make([]T, len(t))
for i, v := range t {
nt[i] = f(v)
}
return nt
}
func main() {
s := []int{1, 2, 3, 4, 5}
fmt.Println(Map(s, func(i int) int {
return i * i
}))
}
在上面的例子中,我们定义了一个泛型函数 Map,它可以接收任何类型的列表和一个函数,然后返回一个新的列表。在 main 函数中,我们传入一个整数列表和一个将整数平方的函数,然后得到一个新的平方列表。
泛型约束
在 Go 1.18 版本引入的泛型特性中,类型参数可使用约束。泛型约束是对泛型参数所能接受的具体类型进行限制的手段,它可以是一个接口或者一个具体的类型。
比如你定义了一个泛型函数, 该函数的参数可以接受任何类型, 但是在函数体内你又需要对这个参数进行加法运算,这种情况下你就需要使用约束将参数类型限制为整型或者浮点型,否则的话不同的数据类型是不能进行运算的。
例如:
type Number interface {
type int, float64
}
func Add[T Number](a, b T) T {
return a + b
}
在上述例子中,我们定义了一个叫做 Number 的泛型约束,它限制了可以接受的类型为 int 或 float64。然后在 Add 函数中,我们使用了这个约束,所以该函数的参数 a 和 b 必须是 int 或 float64 类型。在函数体内,我们进行了加法运算,因为已经提前通过约束限定了,这里就不会有类型错误。
当然,你也可以使用接口作为约束,限制类型必须实现某些方法。这样就可以在你的泛型函数或者类型中使用这些方法了。约束提高了泛型的灵活性,使其适用于更广泛的场景。
泛型推导机制
泛型推导机制(Type Inference)是 Go 1.18 版本引入的一个新特性,主要作用是在调用泛型函数或者使用泛型类型时,可以根据实际传入参数的类型或者上下文环境,自动推导出泛型参数的具体类型。
func PrintSlice[T any](s []T) {
for _, v := range s {
fmt.Println(v)
}
}
func main() {
PrintSlice([]int{1, 2, 3}) // 自动推导泛型参数 T 为 int 类型
}
在调用 PrintSlice 函数时,我们没有指定泛型参数 T 的具体类型。但是由于我们传入的是一个 int 类型的切片,所以 Go 语言的编译器可以自动推导出泛型参数 T 的具体类型为 int。
这种泛型推导机制极大的提高了代码的简洁性和使用便利性,也是 Go 语言泛型特性的一个重要组成部分。
值得注意的是,虽然大多数情况下 Go 语言可以正确的自动推导出泛型参数的类型,但是在某些复杂的场景下,编译器可能无法正确推导出类型,这时就需要显式的指定泛型参数的类型。