海南百度网站建设,宣传片制作公司推荐,网站模板怎么建站,做网站编辑好还是期刊编辑好泛型
Go 并不是一种静止的、一成不变的编程语言。新的功能是在经过大量的讨论和实验后慢慢采用的。最初的 Go1.0发布以来#xff0c;Go语言习惯的模式已经发生了重大变化1.7的context、1.11的modules、1.13 error嵌套等Go的 1.18 版本包括了类型参数的实现#xff0c;也就是…泛型
Go 并不是一种静止的、一成不变的编程语言。新的功能是在经过大量的讨论和实验后慢慢采用的。最初的 Go1.0发布以来Go语言习惯的模式已经发生了重大变化1.7的context、1.11的modules、1.13 error嵌套等Go的 1.18 版本包括了类型参数的实现也就是俗称的泛型泛型虽然很受期待但实际上推荐的使用场景也并没有那么广泛。但是我们作为学习者一定要了解学会遇到了至少不懵逼!
package mainimport fmtfunc main() {stars : []string{wk, ce}printArray(stars)is : []int{1, 2}printArray(is)
}// **我们不限定他类型让调用者自己去定义类型。T ** // []T 形式类型实际类型
/*
**内置的泛型类型any和comparable**
**any:** 表示go里面所有的内置基本类型等价于 interface}
**comparable:** 表示go里面所有内置的可比较类型:int、uint、float、bool、struct、指针等一切可以比较的类型
*/
func printArray[T string | int](arr []T) {for _, a : range arr {fmt.Println(a)}
}小结泛型的作用
泛型减少重复代码并提高类型安全性。使用场景当你需要针对不同类型书写相同逻辑时使用泛型来简化代码是最好的处理方法。
泛型类型
观察下面这个简单的例子:
type s1 []int
var a sl []int{123} // 正确
var b s1 []float321.02.03,0} // 错误因为Intslice的底层类型是[]int浮点类型的切片无法赋值这里定义了一个新的类型IntSlice它的底层类型是 int理所当然只有int类型的切片能赋值给 ntSlice 类型的变量。
接下来如果我们想要定义一个可以容纳 loat32 或 string 等其他类型的切片的话该怎么办?很简单给每种类型都定义个新类型
type sl []int
type s2 []float32
type s3 []float643go但是这样做的问题显而易见它们结构都是一样的只是成员类型不同就需要重新定义这么多新类型。那么有没有一个办法能只定义一个类型就能代表上面这所有的类型呢?答案是可以的这时候就需要用到泛型了:
type slice[T int|float32lfloat64] []T不同于一般的类型定义这里类型名称 Slice 后带了中括号对各个部分做一个解说就是T就是上面介绍过的类型形参(Type parameter)在定义Slice类型的时候T代表的具体类型并不确定类似一个占位符int float32 float64 这部分被称为类型约束(Type constraint)中间的 的意思是告诉编译器类型形参T只可以接收 int 或 float32或float64这三种类型的实参中括号里的 Tint float32 flat64 这一整串因为定义了所有的类型形参(在这个例子里只有一个类型形参T)所以我们称其为 类型形参列表(type parameter list)这里新定义的类型名称叫 slice[T]
这种类型定义的方式中带了类型形参很明显和普通的类型定义非常不一样所以我们将这种类型定义中带 类型形参 的类型称之为 泛型类型
package mainimport fmtfunc main() {//定义一个泛型类型切片type Slice[T int | float64 | float32] []Tvar a Slice[int] []int{1, 2, 3}fmt.Println(a)fmt.Printf(%T, a)var b Slice[float32] []float32{1, 2, 3}fmt.Println(b)fmt.Printf(%T, b)var c Slice[float64] []float64{1, 2, 3}fmt.Println(c)fmt.Printf(%T, c)//定义一个泛型类型maptype myMap[KEY int | string, VALUE any] map[KEY]VALUEvar m1 myMap[string, any] map[string]any{go: 9.9,java: 9.0,}fmt.Println(m1)
}所有类型定义都可使用类型形参所以下面这种结构体以及接口的定义也可以使用类型形参:
// 一个泛型类型的结构体。可用 int 或 sring 类型实例化
type MyStruct[T int | string] struct {Id T 1 uuidName string
}
// 一个泛型接口
type IPrintData[T int | float32 | string] interface {Print(data T)
}
// 一个泛型通道可用类型实参 int 或 string 实例化
type MyChan[T int | string] chan T特殊的泛型类型
这里讨论种比较特殊的泛型类型如下
type wow[T int | string] int
var a wow[int] 1233// 编译正确
var b wow[string] 123 // 编译正确
var c Wow[string] he11o” // 编译错误因为he11o不能赋值给底层类型int这里虽然使用了类型形参但因为类型定义是 type wow[Tint|string] int 所以无论传入什么类型实参实例化后的新类型的底层类型都是int。所以int类型的数字123可以赋值给变量a和b但string类型的字符串“hello”不能赋值给c 这个例子没有什么具体意义但是可以让我们理解泛型类型的实例化的机制
泛型函数
package mainimport fmttype MySlice[T int | float64] []T// Sum 给泛型添加方法
func (s MySlice[T]) Sum() T {var sum Tfor _, v : range s {sum v}return sum
}// Add 泛型函数
func Add[T int | float64 | string](a T, b T) T {return a b
}func main() {var s MySlice[int] []int{1, 2, 3, 4, 5}fmt.Println(s.Sum())var f MySlice[float64] []float64{1.0, 2.1, 3.2, 4.3, 5.7}fmt.Println(f.Sum())fmt.Println(Add(1, 2))fmt.Println(Add(1.2, 2.3))fmt.Println(Add(3333, 4444))
}自定义泛型类型
package mainimport fmt// MyInt 自定义泛型约束
type MyInt interface {int | int8 | int16 | int32 | int64
}func GetMaxNum[T MyInt](a, b T) T {if a b {return a}return b
}func main() {fmt.Println(GetMaxNum(10, 20))
}