泛型:
泛型可以让开发者编写更加灵活、可复用、类型安全的代码。
- 在没有泛型的情况下,Go语言开发者需要使用接口或类型断言等技术来实现类似泛型的功能,但这些方法都有一定的局限性。
- 使用泛型,开发者可以编写可以适用于多种不同类型的函数、数据结构等,而无需针对每种类型都编写一份代码。这样可以提高代码的可读性、可维护性和可扩展性,也能减少代码重复的问题。
- 此外,泛型还可以增强类型安全性,减少因为类型错误引起的编译错误或运行时错误。泛型还可以帮助开发者更好地理解和组织程序逻辑,使得代码更加清晰简洁。
在不使用泛型的情况下如何求计算两数之和
func Add(a int, b int) int {
return a + b
}
如果数值为float呢,还是要像上面一样再定义
func AddFloat32(a float32, b float32) float32 {
return a + b
}
func AddString(a string, b string) string {
return a + b
}
这么多基础类型逐个定义,显然太麻烦,于是泛型来了
格式:在函数后用中括号
声明T为可能出现的类型,中间用符号|
分隔
func main() {
str := []string{"a", "b", "c", "d", "e"}
i := []int{1, 2}
f := []float64{1.1, 2.2}
Result(str)
Result(i)
Result(f)
}
func Result[T int | string | float64](arr []T) {
for _, v := range arr {
fmt.Printf("%T\t%v\n", v, v)
}
}
输出:
string a
string b
string c
string d
string e
int 1
int 2
float64 1.1
float64 2.2
但是要把这么多类型定义出来也有点麻烦,所以Go内置了两个泛型类型any、compareble
any、compareble:
// any:支持go语言所有的内置基本类型,等同空接口
type any = interface{}
// comparable:支持go语言可以比较的类型:int、uint、float、bool、struct、指针等一切可以比较的类型
type comparable interface{ comparable }
使用any、compareble改一下上面演示的代码
func main() {
str := []string{"a", "b", "c", "d", "e"}
i := []int{1, 2}
f := []float64{1.1, 2.2}
Result(str)
Result(i)
Result(f)
}
func Result[T comparable](arr []T) {
for _, v := range arr {
fmt.Printf("%T\t%v\n", v, v)
}
}
func Result[T any](arr []T) {
for _, v := range arr {
fmt.Printf("%T\t%v\n", v, v)
}
}
泛型函数、方法:
泛型函数格式:func 函数名 [要约束的数据类型] (形参列表)(返回值){}
func Add[T int | float64](a, b T) T {
return a + b
}
func main() {
fmt.Println(Add[int](1, 2))
fmt.Println(Add[float64](1, 2))
// 在函数调用上不加参数类型是可以完成自动推导的
fmt.Println(Add(1, 2))
fmt.Println(Add(1, 2))
}
泛型方法:
// []T 初始化类型是T类型
type MySlice[T int | float64] []T
func (s MySlice[T]) Add() T {
var sum T
for _, v := range s {
sum += v
}
return sum
}
func main() {
var s MySlice[int] = []int{1, 2, 3, 4}
fmt.Println(s.Add())
}
自定义泛型:
自定义接口,将需要的数据类型逐个定义
// 自定义类型
type Genericity interface {
int | ~int8 | int16 | int32 | int64
}
// 给类型起别名以后就不能被识别了,要在自定义类型中加上波浪线会自动识别
type int8AA int8
// 注意:最后面这个int表示初始化必须是int类型
type special[T int | string] int
func main() {
var a special[int] = 1 // 编译正确
var b special[string] = 1 // 编译正确
var c special[string] = "1" // 编译错误,初始化必须是int类型
fmt.Printf("%T\n", a) // main.special[int]
fmt.Printf("%T\n", b) //main.special[string]
var d int8AA = 1
var e int8AA = 2
fmt.Println(Add(d, e))
}
func Add[T Genericity](a, b T) T {
if a > b {
return a
}
return b
}
定义切片为泛型类:
// Slice为T类型,约束必须是int或float32
type Slice[T int | float32] []T
func main() {
// 指定初始化为int类型
var s Slice[int] = []int{1, 2, 3, 4}
fmt.Printf("%T\n", s) // main.Slice[int]
// 指定初始化为float32类型
var s2 Slice[float32] = []float32{1.1, 2.2, 3.3, 4.4}
fmt.Printf("%T\n", s2) //main.Slice[float32]
}
定义Map为泛型:可以直接将map作为函数参数传递
// 定义了一个Map,指定key是string类型,value是int类型
type MyMap[key string, value int] map[key]value
// 定义了一个Map,指定key是string或int类型,value也是string或int类型
type MyMap2[key string | int, value string | int] map[key]value
func main() {
// 初始化使用自定义类型Map
var m MyMap[string, int] = map[string]int{
"韩信": 1,
"李白": 2,
}
result(m)
}
// 将自定义Map泛型类作为函数参数
func result(m MyMap[string, int]) {
fmt.Println(m)
}