泛型:

泛型可以让开发者编写更加灵活、可复用、类型安全的代码。

  • 在没有泛型的情况下,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)
}