【Go语言成长之路】泛型入门
文章目录
- 泛型入门
- 一、前提
- 二、创建项目
- 三、调用非泛型函数
- 四、调用泛型函数处理多种类型
- 五、不使用类型参数调用泛型函数
- 六、声明类型约束为接口
泛型入门
本教程介绍了Go中泛型(generics)的基础知识,并且将声明两个简单的非泛型函数,然后在单个泛型函数中捕获相同的逻辑。
一、前提
Go1.18
以及更高的版本
二、创建项目
创建一个名为 generics 的目录:
~$ mkdir generics
~$ cd generics
创建模块:
~/generics$ go mod init example/generics
go: creating new go.mod: module example/generics
三、调用非泛型函数
在此步骤中,将会添加两个函数,每个函数将map的值相加并返回总计。
注:这里需要声明两种不同类型的maps: 一个用于存储int64的值,一个用于存储float64的值。
接下来就是编写代码:
package mainimport "fmt"// SumInts takes a map of string to int64 values.
func SumInts(m map[string]int64) int64 {var s int64for _, v := range m {s += v}return s
}// SumFloats takes a map of string to float64 values.
func SumFloats(m map[string]float64) float64 {var s float64for _, v := range m {s += v}return s
}func main() {// Initialize a map for the integer valuesints := map[string]int64{"first": 34,"second": 12,}// Initialize a map for the float valuesfloats := map[string]float64{"first": 35.98,"second": 26.99,}// Call the two functions you declared earlier to find the sum of each map’s values// and print the resultfmt.Printf("Non-Generic Sums: %v and %v\n",SumInts(ints),SumFloats(floats))
}
运行代码:
~/generics$ go run .
Non-Generic Sums: 46 and 62.97
四、调用泛型函数处理多种类型
在此步骤中,将通过编写一个泛型函数,该函数可以接受包含int或者float的map。
要想实现该功能,有以下几点需要考虑:
- 支持任意类型: 需要一种方法来声明支持的类型
- 判断类型: 判断它是使用int或者flaot的map。
- 处理类型: 除了普通的函数参数依赖还需要有
type
参数,使得能够处理不同类型的参数。可以通过type
参数和普通参数一起来调用函数。 type
类型约束: 每个type参数都有一个type约束,指定调用代码可用的type参数。如果type
参数的约束不允许指定的type
参数,则代码将无法编译。
注: type参数必须支持泛型代码对其执行的所有操作。
接下来就是编写代码:
package mainimport "fmt"// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
// Declare a SumIntsOrFloats function with two type parameters (inside the square brackets), K and V
// and one argument that uses the type parameters, m of type map[K]V. The function returns a value of type V.
// Specify for the K type parameter the type constraint comparable.the comparable constraint is predeclared in Go. It allows any type whose values may be used as an operand of the comparison operators == and !=.
// Specify for the V type parameter a constraint that is a union of two types: int64 and float64. Using | specifies a union of the two types, meaning that this constraint allows either type.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {var s Vfor _, v := range m {s += v}return s
}func main() {// Initialize a map for the integer valuesints := map[string]int64{"first": 34,"second": 12,}// Initialize a map for the float valuesfloats := map[string]float64{"first": 35.98,"second": 26.99,}fmt.Printf("Generic Sums: %v and %v\n",SumIntsOrFloats[string, int64](ints),SumIntsOrFloats[string, float64](floats))
}
运行代码:
~/generics$ go run .
Generic Sums: 46 and 62.97
五、不使用类型参数调用泛型函数
当 Go 编译器可以推断出您要使用的类型时,可以在调用代码中省略类型参数。编译器从函数参数的类型推断类型参数。
请: 如果需要调用没有参数的泛型函数,则需要在函数调用中包含类型参数。
修改上面的代码中的打印部分代码:
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",SumIntsOrFloats(ints),SumIntsOrFloats(floats))
结果和上面一致。
六、声明类型约束为接口
将类型约束声明为一个接口。
// Declare the Number interface type to use as a type constraint.
type Number interface {// Declare a union of int64 and float64 inside the interface.int64 | float64
}
这样,当想要将类型参数约束为 int64
或 float64
时,可以使用此 Number
类型约束,而不是写出 int64 |float64
。
之后在该接口粘贴如下代码:
// SumNumbers sums the values of map m. It supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {var s Vfor _, v := range m {s += v}return s
}
在main.go
中,可以通过如下方式来调用:
fmt.Printf("Generic Sums with Constraint: %v and %v\n",SumNumbers(ints),SumNumbers(floats))
运行代码:
~/generics$ go run .
Generic Sums with Constraint: 46 and 62.97