实验Go 泛型新特性:
一.先决条件: 运行以下两行命令去下载支持运行泛型的go编译环境。
go get golang.org/dl/go1.18beta1@latest
// 下载go1.18beta1 可执行文件到go配置的bin目录下
go1.18beta1 download
// 检查是否下载成功
go1.18beta1 version
//测试的代码要用go1.18beta1 来run。
二. 不用泛型实现把一个map里的所有value求和,且这个value可能是int64或者float64。
代码示例:
package main
/**
以下示例显示了用两个不同的Sum类型方法处理累加
*/
import "fmt"
// SumInts 累加map里的int64类型的value
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// SumFloats 累加map里的float64类型的value
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
func main() {
// 初始化测试int64类型累加的map
ints := map[string]int64{
"first": 34,
"second": 12,
}
// 初始化测试float64类型累加的map
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Non-Generic Sums: %v and %v\n",
SumInts(ints),
SumFloats(floats))
}
NOTE: 使用刚下载命令或者直接go run都可以:
三.目标: 使用泛型函数(generic function)去处理多种类型。
以下示例用一个可以同时接收interger 和 float的泛型map,替换掉上述使用两个方法去处理sumxxx功能。
要实现这个目标,需要明确以下步骤:
- 定义泛型类型去同时支持多种类型。
- 编写函数时,除了原来的函数定义外,还需要定义 type parameters 泛型的类型,这个定义可以让方法使用泛型的特性,使它可以同时接收不同类型的参数。
- 类型参数(type parameters)可以限制一组类型,但是在编译时,只能代表一种类型,这个类型是调用这个方法传入的类型,如果传入的类型不符合类型参数限制的,就无法编译通过。
类型参数type parameters怎么定义?
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V
当我们定义的函数需要使用泛型时,在函数名和实际传参列表之间需要用[ ] 给出用到的类型参数的定义。 如果支持多种类型,用 | 隔开。
代码示例:
package main
import "fmt"
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var result V
for _,v := range m {
result += v
}
return result
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := 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))
}
Note: 如果代码是在goland里编写的,要注意goland的编译环境会提示报错,这里运行要在commandline里使用 go1.18beta1 run main.go 来运行。
从示例中可以看到,不仅函数定义要添加[K Ktype, V Vtype]的限制,在调用方法时,也要给K V限制传入的类型。
实际上,这里在调用方法时不传类型声明也可以,编译器会有类型推断的功能,把fmt改为一下的方式调用同样可以。
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats(ints),
SumIntsOrFloats(floats))
四. 泛型参数使用接口声明:
这里的V的类型可以是多种类型的参数,我们可能需要支持不同类型的sum操作,每个方法都这样定义很不方便,以下使用接口声明泛型参数支持的类型的示例:
type Number interface {
int64 | float64
}
定义一个type Number 的接口类型,把所有支持的参数都在这里定义。
函数的声明修改:
func SumNumbers[K comparable, V Number](m map[K]V) V {
var result V
for _,v := range m {
result += v
}
return result
}
这里V的类型改成了接口的类型。
调用方法打印同样没有问题:
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
SumNumbers(ints),
SumNumbers(floats))
完整的代码示例:
https://github.com/hinss/golearn/tree/master/generic_test