# 泛型(1.18) ## 泛型的概念理解 Go语言他是在进化的,不断的迭代,优化功能! 1.0 - 1.19 (1.20)/ 和最开始Go语言比起来发生了很多变化。 1.7 Context 1.11 modules 1.13 error嵌套 1.18 泛型(类型参数) https://segmentfault.com/a/1190000041634906 泛型的出现,很受期待,但是很少用!场景不多。 > 打印一个切片(传递不同的参数string 、int) ```go package main import ( "fmt" ) func main() { is := []int{1, 2} //strs := []string{"kuangshen", "xuexiangban"} printSlice(is) //printSlice(strs) } // 如何实现一个方法可以打印上面不同类型的切片呢? 反射 func printSlice(s interface{}) { // 断言 x.(T), 如果x实现了T,那么就将 x转换为T类型 for _, v := range s.([]string) { fmt.Println(v) } } ``` 这种情况,传递的参数进来,需要自己做N种判断来进行适配。 > 泛型 思考:不限定参数的类型,让调用的人自己去定义类型。 ```go // 参数的类型是不确定的,让用户自己指定。 // 泛型也是使用 [],和数组很像! // func printSlice[T any](s []T) { for _, v := range s { fmt.Println(v) } } ``` printSlice , [T any] 泛型的约束!由于我们不确定这个函数的参数类型。我们希望用户给我们传递这个值。 ``` print[T string](name T) print[T string|int](name T) func Min[T int|int8|int16|int32|int64](a,b T){ } ``` 泛型的作用: 1、减少重复性的代码,提高安全性 - **针对不同的类型,写了相同逻辑的代码,我们就可以通过泛型来简化代码!** 2、在1.18版本之前 反射 来实现。 泛型并不能完全取代反射! 泛型:多的多个类型(类型可以是不确定的) ``` var 变量名 数据类型 = 值 var 变量名 T = 值 // 泛型的逻辑 ``` ## 泛型类型 ```go package main import "fmt" // s1 是我们自己定义的类型 ,本质 []int type s1 []int type s2 []float64 type s3 []float32 // 我们定义的结构都是一样的,只是它的类型不同,就需要重新定义这么多的类型。 // 思考:是否有一种机制,只定义一个类型就可以代表上面的所有类型? // 泛型:类型 参数化了! 参数:人为传递的 /* 1、T 说白了就是一个占位符,类型的形式参数,T是不确定的,需要在使用的时候进行传递。 2、由于T类型是不确定的,我们需要加一些约束 int|float64|float32 。告诉编译器我这个T,只接受 int、float64、float32 类型 3、我们这里定义的类型是什么?Slice[T] */ // 这种类型的定义方式,带了类型形参,和普通定义类型就完全不同的。 // 普通的定义类型,这个类型只能代表本身一个,泛型类型,我们可以实现,参数类型传递。 // 我们可以在使用的时候来定义类型。 // 语法糖:简化开发 type Slice[T int | float64 | float32] []T func main() { var a Slice[int] = []int{1, 2, 3} fmt.Printf("%T\n", a) // Slice[int] var b Slice[float64] = []float64{1.0, 2.0, 3.0} fmt.Printf("%T\n", b) // Slice[float64] // 不能够赋值(string 不在T的约束当中,不能实例化的) //var c Slice[string] = []string{"kuangshen","xxx"} // T是占位符,在使用的时候,必须要实例化为具体的类型。 //var d Slice[T] = []int{1,2,3} } func test(name interface{}) { fmt.Println(name) } ``` 泛型的类型使用 ```go package main import "fmt" // 泛型可以用在所有有类型的地方 type MyStruct[T int | string] struct { Id T Name string } type IprintData[T int|float64|string] interface { Print(data T) } // 通道 type MyChan[T int|string|float64] chan T func main() { //T 泛型的参数类型的属性可以远不止一个,所有东西都可以泛型化。 // map(int)string // map[KEY]VALUE 类型形参(参数是不确定) KEY 、VALUE // KEY int | string , VALUE float32 | float64 约束 // 类型的名字 MyMap[KEY,VALUE], 通过这一个类型,来代表多个类型!--> 泛型 type MyMap[KEY int | string | float64, VALUE float32 | float64 | int] map[KEY]VALUE // map [string]float64 var score MyMap[string, float64] = map[string]float64{ "go": 9.9, "java": 8.0, } fmt.Println(score) } ``` > 特殊的泛型 ```go package main func main() { // 特殊的泛型类型,泛型的参数时多样的,但是实际类型定义就是int type AAA[T int|string] int var a AAA[int] = 123 var b AAA[string] = 123 //var c AAA[string] = "hello" // 底层类型是int } ``` 这里虽然使用了泛型。但是底层类型就是int,所以无论传什么都可以的,但是赋值,只能是int、 这个例子没什么意义。 ## 泛型函数 单纯的泛型没啥意义。和函数结合使用, 可以使用调用者(调用者的类型可以自定义,就可以实现泛型。) ```go package main import ( "fmt" ) type MySlice[T int | float32 | int64] []T func main() { var s MySlice[int] = []int{1, 2, 3, 4} fmt.Println(s.sum()) var s1 MySlice[float32] = []float32{1.0, 2.0, 3.0, 4.4} fmt.Println(s1.sum()) } // 调用者,类型是不确定的,用户传什么,她就实例化什么。 类型参数化了 , 泛型 // 没有泛型之前, 反射: reflect.ValueOf().Kind() , 也需要很多if,本质是逻辑相同的,只是类型不同! func (s MySlice[T]) sum() T { var sum T for _, v := range s { sum += v } return sum } ``` 泛型可以增加代码的灵活性,降低了可读性! ```go package main import ( "fmt" ) func main() { var a int = 1 var b int = 2 fmt.Println(Add[int](a, b)) var c float32 = 1.1 var d float32 = 2.2 fmt.Println(Add[float32](c, d)) // 每次都去写T的类型是很麻烦的,支持自动推导! // Go的泛型语法糖:自动推导 (本质,就是编译器帮我们加上去了,在实际运行,这里T还是加上去的) fmt.Println(Add(a, b)) // T : int fmt.Println(Add(c, d)) // T : float32 } // 真正的Add实现,传递不同的参数都是可以适配的! Add[T] T在调用的时候需要实例化 // 这种带了类型形参的函数就叫做泛型函数,极大的提高代码的灵活心,降低阅读性! func Add[T int | float32 | float64](a T, b T) T { return a + b } ``` 1、泛型类型 (自定义类型结合泛型使用) 2、泛型作为接收者 (实现函数的灵活变化) 3、泛型函数(参数是泛型) 计算机底层的运行,绕不过的,你只能简化代码开发,不能简化实际运行操作! ## 自定义泛型 由于约束有时候很多,我们可以定义一些自己的泛型约束(本质是一个接口) ```go package main // 泛型的约束提取定义 type MyInt interface { int|float32|int8|int32 // 作用域泛型的,而不是一个接口方法 } // 自定义泛型 func main() { var a int = 10 var b int = 20 GetMaxNum(a,b) } func GetMaxNum[T MyInt](a,b T) T { if a>b { return a } return b } ``` 内置泛型类型: any (就是一个泛型,表示了go所有的内置类型。interface{}) comparable :表示所有可以比较的类型 新符号 ~,和类型一起出现的,表示支持该类型的衍生类型! ```go package main import "fmt" // int8 衍生类型 type int8A int8 type int8B = int8 // ~ 表示可以匹配该类型的衍生类型 type NewInt interface { ~int8 } // ~ func main() { var a int8A = 10 var b int8A = 10 fmt.Println(GetMax(a, b)) } func GetMax[T NewInt](a, b T) T { if a > b { return a } return b } ``` 总结泛型: 1、类型参数 (T) 2、类型集合 (T,K )map slice 3、类型推断 (简化开发)