泛型入门
什么是泛型?
泛型允许你编写可以处理多种类型的函数或类型,而不需要为每种类型单独编写代码。通过泛型,调用代码可以提供具体的类型参数。
为什么需要泛型?
假设我们要计算map中所有值的总和,传统方式需要为不同类型写不同的函数:
go
// 处理int64类型的map
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// 处理float64类型的map
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
这样会产生重复代码,维护困难。
使用泛型解决问题
1. 基本泛型函数
go
// 使用泛型,一个函数处理多种类型
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
关键概念:
[K comparable, V int64 | float64]
是类型参数K comparable
表示K必须是可比较的类型(可以用作map的key)V int64 | float64
表示V只能是int64或float64类型|
表示类型联合(union)
2. 调用泛型函数
go
func main() {
ints := map[string]int64{"first": 34, "second": 12}
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))
// 类型推断(推荐)- 编译器自动推断类型
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats(ints),
SumIntsOrFloats(floats))
}
3. 定义类型约束
为了重用和简化,可以将类型约束定义为接口:
go
// 定义类型约束接口
type Number interface {
int64 | float64
}
// 使用自定义约束
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
完整示例
go
package main
import "fmt"
type Number interface {
int64 | float64
}
func main() {
ints := map[string]int64{"first": 34, "second": 12}
floats := map[string]float64{"first": 35.98, "second": 26.99}
fmt.Printf("Generic Sums: %v and %v\n",
SumNumbers(ints),
SumNumbers(floats))
}
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
核心要点
- 类型参数:在函数名后用方括号
[]
声明 - 类型约束:限制类型参数可以使用的具体类型
- 类型推断:Go编译器通常可以自动推断类型,无需显式指定
- 类型联合:使用
|
连接多个允许的类型 - comparable:内置约束,表示可比较的类型(可用作map的key或进行 == 比较)
使用场景
- 需要处理多种相似类型的通用算法
- 避免为不同类型编写重复代码
- 提高代码复用性和类型安全性
泛型让Go代码更加灵活和可重用,同时保持类型安全!