泛型(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)
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种判断来进行适配。
泛型
思考:不限定参数的类型,让调用的人自己去定义类型。
// 参数的类型是不确定的,让用户自己指定。
// 泛型也是使用 [],和数组很像!
//
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 = 值 // 泛型的逻辑
泛型类型
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)
}
泛型的类型使用
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)
}
特殊的泛型
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、
这个例子没什么意义。
泛型函数
单纯的泛型没啥意义。和函数结合使用, 可以使用调用者(调用者的类型可以自定义,就可以实现泛型。)
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
}
泛型可以增加代码的灵活性,降低了可读性!
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、泛型函数(参数是泛型)
计算机底层的运行,绕不过的,你只能简化代码开发,不能简化实际运行操作!
自定义泛型
由于约束有时候很多,我们可以定义一些自己的泛型约束(本质是一个接口)
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 :表示所有可以比较的类型
新符号 ~,和类型一起出现的,表示支持该类型的衍生类型!
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、类型推断 (简化开发)