当前位置:职场发展 > Go1.18泛型全部教程

Go1.18泛型全部教程

  • 发布:2023-10-07 02:22

目录
  • Go1.18 所有泛型教程
    • 1。什么是泛型
    • 2. Golang 中的泛型
    • 3。泛型语法详解
      • 3.1 泛型语法
      • 3.2 什么是约束
      • 3.3 自定义约束
    • 1.声明一个通用函数
    • 2。声明一个通用切片
      • www.sychzs.cn/x/exp 包
    • 3。声明通用地图
    • 4。声明通用通道
    • 5。泛型约束
      • 5.1 使用接口中指定的类型来约束泛型函数的参数
      • 5.2 使用接口中指定的类型方法来约束泛型参数
      • 5.3 使用接口中指定的方法和类型对泛型参数进行双重约束
      • 5.4 使用具有可比约束的泛型进行判断和比较
    • 6。声明一个通用结构

Go1.18泛型所有教程

一 什么是仿制药

泛型英文是Generics,是函数的参数或者容器元素的类型。它们支持更广泛的类型,不再是特定的类型。

以下只是泛型的简单介绍

  • 函数和类型声明的语法接受类型参数
  • 参数化函数和类型可以通过方括号中的类型参数列表进行实例化
  • 接口类型的语法现在允许嵌入任意类型以及 Union 和 ~T 类型元素。这些接口只能用作类型约束。接口现在可以定义一组类型和一组方法
  • 新的预声明标识符any是空接口的别名。您可以使用任何代替接口{}
  • 新的预声明标识符Comparable表示可以使用==或!=进行比较的所有类型的接口,可以用作类型约束
  • 有三个使用泛型的实验包。 www.sychzs.cn/x/exp 下的所有包都是实验性或过时的包,不建议使用
    • www.sychzs.cn/x/exp/constraints
    • www.sychzs.cn/x/exp/slices
    • www.sychzs.cn/x/exp/maps

在Golang、Java、C++等静态语言中,需要严格定义传入变量的类型。你不能随心所欲。例如,在 golang 中:

func Sum(a, b int) int {
返回 a + b
}

在Sum函数中,不仅参数a和b的变量类型要严格定义,返回值的类型也需要严格定义,所以调用时只能传入int类型:

Sum(1, 2) // 3

如果传入其他类型的变量,会报错:

fmt.Println(Sum(1.23, 2.54));
./main.go:33:18:不能使用 1.23(无类型浮点常量)作为 Sum 参数中的 int 值(已截断)
./main.go:33:24:不能使用 2.54(无类型浮点常量)作为 Sum 参数中的 int 值(已截断)

因此,如果 Golang 开发者想要开发一个类似于两个 float 类型变量相加的函数,就只能再写一个函数:

func SumFloat(a, b float) float {
返回 a + b
}

或者写一个通用的Sum函数,利用接口反射来判断:

func Sum(a, b 接口{}) 接口{} {
开关 a.(类型) {
案例整数:
a1 := a.(int)
b1 := b.(int)
返回 a1 + b1
案例 float64:
a1 := a.(float64)
b1 := b.(float64)
返回 a1 + b1
默认:
返回零
}
}

这种情况下,不仅代码大量重复,而且频繁的类型转换不仅导致性能低下,安全性也低。

于是仿制药诞生了。

然而,仿制药是一把双刃剑。它们在给开发者带来便利的同时,也带来了编译和效率问题,因为泛型需要系统对变量的类型进行翻转和计算,而这是不可见的。这会增加编译时间,降低运行效率。

II Golang 中的泛型

首先我们看一下Golang 1.18版本中如何使用泛型来实现Sum函数

func Sum[T int|float64](a,b T) T {
返回 a + b
}

然后再次调用:

fmt.Println(Sum[int](1, 2)) //3
fmt.Println(Sum[float64](1.23, 2.54)) //3.77

我们先不明白函数中各个组成部分的含义。直接看代码就简单多了。该函数还实现了多种类型的函数。

3 通用语法详细解释

3.1 通用语法

MyType[T1 约束 1 |约束 2,T2 约束 3...] ...

泛型的语法非常简单,与上面类似,其中:

MyType 可以是函数名称、结构名称、类型名称...
T1、T2……为通用名称,可随意选择。
Constraint就是约束的意思,也是泛型中最重要的概念。接下来将详细解释约束。
使用|分离多个约束,只要T满足其中之一即可(例如T1可以是constraint1和constraint2中的任意一个)

3.2 什么是约束

约束就是限制范围。约束的作用就是限制范围,将T限制在某个范围内

而常用的范围,我们自然想到:

any(接口{},可以接收任何类型,多方便啊!)
Interger(全是int,多方便啊,int64 int32...全部捕获)
浮动(同上)
可比较(所有可比较类型,我们可以为所有可比较类型自定义一些方法)
……

这些约束要么被正式定义为内置类型,要么包含在约束包中!!!

以下为builtin.go官方源码的部分内容:

//any 是interface{} 的别名,在所有方面与interface{} 等价。
输入任意 = 接口{}
//可比较是由所有可比较类型实现的接口
//(布尔值、数字、字符串、指针、通道、接口、
// 可比较类型的数组、字段都是可比较类型的结构)。
// 类似的接口只能用作类型参数约束,
// 不作为变量的类型。
类型 可比较 可比较

以下是constraints.go官方源码的部分内容:

//整数是允许任何整数类型的约束。
// 如果 Go 的未来版本添加新的预声明整数类型,
// 该约束将被修改以包含它们。
类型整数接口{
签名|未签名
}
// Float 是允许任何浮点类型的约束。
// 如果 Go 的未来版本添加新的预声明浮点类型,// 该约束将被修改以包含它们。
类型浮点接口{
〜float32 | 〜float64
}
//......

3.3 自定义约束

以下是约束包中的官方源代码:

类型签名接口{
〜int | 〜int8 | 〜int16 | 〜int32 | ~int64
}

有符号约束就是这样写的,我们需要掌握的点如下:

使用interface{}自定义约束
使用|您可以在约束中包含不同的类型,例如 int、int8、int64 都满足 Signed 约束
你可能会有疑问,什么是~???我知道int,但我不知道~int???没关系,其实~很简单,就是模糊匹配的意思,例如:
类型 MyInt int64
此时MyInt并不等同于int64类型(Go语言特性)
如果我们使用int64来约束MyInt,Myint不满足约束
如果我们用~int64来约束MyInt,那么Myint就满足约束(即~int64只要求类型的底层是int64,属于模糊匹配)
为了健壮性,官方自然把所有类型都加在前面了~

例如:

type My_constraint_Num 接口 {
〜int64 | 〜float64
}

1。声明一个通用函数

包装主
导入“fmt”
函数 printSlice[T int | int64 |浮动64 |字符串](数据[]T) {
对于 _, v := 范围数据 {
fmt.Println(v)
}
}
func printSliceAny[T 任何](数据 []T) {
对于 _, v := 范围数据 {
fmt.Println(v)
}
}
//多个泛型参数语法:func printSliceDemo01[T, M 任何](data01 []T, data02 []M) {
fmt.Println("printSliceDemo01================")
fmt.Println(data01)
fmt.Println("printSliceDemo01================")
fmt.Println(data02)
}
//[]中写入一个类型,传入的data01和data02必须是相同的数据类型
func printSliceDemo02[T 任何](data01 []T, data02 []T) {
fmt.Println("printSliceDemo02================")
fmt.Println(data01)
fmt.Println("printSliceDemo02================")
fmt.Println(data02)
}
//[]中写入一个类型,传入的data01和data02必须是相同的数据类型
func printSliceDemo03[T 任何](data01, data02 []T) {
fmt.Println("printSliceDemo03================")
fmt.Println(data01)
fmt.Println("printSliceDemo03================")
fmt.Println(data02)
}
func printSliceDemo04[T 任何,M 任何](data01 []T,data02 []M) {
fmt.Println("printSliceDemo04================")
fmt.Println(data01)
fmt.Println("printSliceDemo04================")
fmt.Println(data02)
}
函数主() {
//显示类型调用
printSlice[int]([]int{66, 77, 88, 99, 100})printSlice[float64]([]float64{1.1, 2.2, 5.5})
printSlice[string]([]string{"烤鸡", "烤鸭", "烤鱼", "烤面筋"})
// 省略显示类型调用
printSlice([]int64{55, 44, 33, 22, 11})
printSliceAny([]int64{55, 44, 33, 22, 11})
printSliceDemo01([]int64{55, 44, 33, 22, 11}, []string{"烤鸡", "烤鸭", "烤鱼", "烤面筋"})
printSliceDemo02([]int64{55, 44, 33, 22, 11}, []int64{55, 44, 33, 22, 11})
printSliceDemo03([]int64{55, 44, 33, 22, 11}, []int64{55, 44, 33, 22, 11})
printSliceDemo04([]int64{55, 44, 33, 22, 11}, []string{"烤鸡", "烤鸭", "烤鱼", "烤面筋"})
}

[T any] 参数的类型,表示该函数支持任意T类型;底层是 type any = interface{}

多个通用参数语法:

[T, M 任意]
[任意,任意]
[T 任意,M 相当]

调用此通用函数时

可显示指定类型参数

例如:printSlice[int]([]int{66, 77, 88, 99, 100})

您还可以省略显示类型并自动推断类型

printSlice([]int64{55, 44, 33, 22, 11})

2。声明一个通用切片

具有类型参数的类型称为泛型类型。接下来定义一个新的类型向量,其基础类型是切片类型。它可以存储任何类型的切片。要使用泛型类型,必须首先实例化它,这意味着为类型参数指定一个实际参数。

包装主
进口 (
“FMMT”
“种类”
“弦”
“www.sychzs.cn/x/exp/constraints”
)
类型向量[T 任意] []T
func printSlice[T 任何](数据 []T) {
fmt.Println(数据)
}
函数主() {
//圆顶01()
//排序SliceDome()
//包含SliceDome()
findFuncDemo()
//filterSliceDome()
//包含[可比较]([int]{58, 1881},58)
//测试最小最大()
}
函数圆顶01() {
v := 向量[int]{58, 1881}
打印切片(v)
v2 := vector[string]{"烤鸡", "烤鸭", "烤鱼", "烤面筋"}
打印切片(v2)
v3 := 向量[float64]{10.2, 2.5}
打印切片(v3)
var v4 向量[int] = []int{1, 2, 3}
v4[2] = 4
打印切片(v4)
}
函数 sortSliceDome() {
floatSlice := []float64{2.3, 1.2, 0.2, 51.2}
sortSlice(floatSlice, "asc")
fmt.Println(floatSlice)
stringSlice := []string{"z", "a", "b"}
sortSlice(stringSlice, "asc")
fmt.Println(stringSlice)
intSlice := []int{0, 3, 2, 1, 6}
sortSlice(intSlice, "desc")
fmt.Println(intSlice)
}// 切片排序顺序 asc|desc
func sortSlice[T Constraints.Ordered](s []T, 顺序字符串) {
if strings.ToUpper(order) == "ASC" ||订单==“”{
sort.Slice(s, func(i, j int) bool {
返回 s[i] < s[j]
})
} 别的 {
sort.Slice(s, func(i, j int) bool {
返回 s[i] > s[j]
})
}
}
func ContainsSliceDome() {
floatSlice := []float64{2.3, 1.2, 0.2, 51.2}
fmt.Println(ContainsSlice(floatSlice, 2.3))
stringSlice := []string{"z", "a", "b"}
fmt.Println(ContainsSlice(stringSlice, "c"))
intSlice := []int{0, 3, 2, 1, 6}
fmt.Println(ContainsSlice(intSlice, 0))
}
// ContainsSlice 是否包含
func ContainsSlice[Econstraints.Ordered](s []E, v E) bool {
for _, vs := 范围 s {
如果 v == vs {
返回真
}
}
返回错误
}
函数 findFuncDemo() {
fmt.Println(FindFunc([]int{1, 2, 3, 4, 5, 6}, 2)) //1
}
//FindFunc查找元素
//该方法用于查找给定元素是否存在于已知切片中。如果存在,则返回该元素所在切片的索引。如果不存在,则返回-1。
//支持泛型类型:可比较。也就是说,属于同一泛型类型的不同元素必须具有可比较性。
func FindFunc[T 可比较](a []T, v T) int {
对于 i, e := 范围 a {如果 e == v {
返回我
}
}
返回-1
}
func filterSliceDome() {
网站 := []string{"http://www.sychzs.cn", "https://www.sychzs.cn", "https://www.sychzs.cn"}
httpsWebsites := FilterSlice(网站, func(v string) bool {
return !strings.HasPrefix(v, "https://")
})
fmt.Println(https网站)
httpsWebsites2 := FilterSlice(网站, func(v string) bool {
返回 strings.HasPrefix(v, "https://")
})
fmt.Println(httpsWebsites2)
数字 := []int{1, 2, 3, 4, 5, 6}
divisibleBy2 := FilterSlice(数字, func(v int) bool {
返回 v%2 == 0
})
fmt.Println(divisibleBy2)
// 输出:
//[https://www.sychzs.cn https://www.sychzs.cn]
//[2 4 6]
}
//FilterSlice过滤出符合确定方法的数据
func FilterSlice[T any](slice []T, f func(T) bool) []T {
变量 n []T
for _, e := 范围切片 {
如果 f(e) {
n = 附加(n,e)
}
}
返回n
}
函数 testMinMax() {
vi := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
结果 := 最大值(vi)
fmt.Println(结果)
vi = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
结果=最小值(vi)
fmt.Println(结果)
// 输出
//10//1
}
类型最小最大接口{
〜int | 〜int8 | 〜int16 | 〜int32 | 〜int64 | 〜uint | 〜uint8 | 〜uint16 | 〜uint32 | 〜uint64 | 〜uintptr | 〜float32 | 〜float64
}
func Max[T minmax](a []T) T {
米 := a[0]
对于 _, v := 范围 a {
如果 m < v {
米=v
}
}
返回米
}
func Min[T minmax](a []T) T {
米 := a[0]
对于 _, v := 范围 a {
如果 m > v {
米=v
}
}
返回米
}

www.sychzs.cn/x/exp 包

文档地址:
https://www.sychzs.cn/www.sychzs.cn/x/exp

注:
不建议使用此套件:
详情请参考以下文档:
关于golang:Go-118将删除泛型的约束包
https://www.sychzs.cn/guan-yu-golang-zhong-bang-go118-jian-yi-chu-yong-yu-fan-xing-de-constraints-bao.html

www.sychzs.cn/x下的所有包的源码独立于Go源码的主干分支,不包含在Go二进制安装包中。如果需要使用www.sychzs.cn/x下的包,可以使用go get来安装。
www.sychzs.cn/x/exp 下的所有软件包都是实验性或过时的软件包,不建议使用。
因为泛型的存在,同一个函数对于不同类型的切片可以节省一段代码。如果要使用切片泛型相关操作,建议复制www.sychzs.cn/x/exp中的函数使用或修改

constraints包一共定义了6种接口类型:Signed、Unsigned、Integer、Float、Complex和Ordered,可用于泛型中的类型约束。

官方还推出了一些官方包,方便泛型的使用,如下:

//constraints定义了一组与类型参数一起使用的约束
封装限制
// 有符号是一个允许任何有符号整数类型的约束。
类型签名接口 { ... }
// 无符号是一个允许任何无符号整数类型的约束。
类型无符号接口 { ... }
// Integer 是一个允许任何整数类型的约束。
类型 整数 接口 { ... }
// Float 是一个允许任何浮点类型的约束。
类型 Float 接口 { ... }
// Complex 是一个允许任何复杂数值类型的约束。
类型 复杂接口 { ... }
// Ordered 是一个允许任何有序类型的约束:任何支持运算符 < <= >= > 的类型。
类型有序接口 { ... }

的使用示例如下:

包装主
进口 (
“FMMT”
“www.sychzs.cn/x/exp/maps”
“www.sychzs.cn/x/exp/slices”
“种类”
)
函数主() {
等圆顶()
//包含Dome()
//包含FuncDome()
//插入圆顶()
}
// EqualDome 是否等于
函数 EqualDome() {
var m1 = 地图[int]int{1: 2, 2: 4, 4: 8, 8: 16}
想要键 := []int{1, 2, 4, 8}
gotKeys := 地图.Keys(m1)
排序.Ints(gotKeys)
//gotKeys 是否等于wantKeysfmt.Println(slices.Equal(gotKeys, WantKeys))
var m2 = map[int]string{1: "a", 2: "b", 4: "c", 8: "d"}
WantValsAsc := []字符串{"a", "b", "c", "d"}
WantValsDesc := []string{"d", "c", "b", "a"}
gotVals := 地图.Values(m2)
sort.Strings(gotVals) // 升序
//gotKeys 是否等于wantKeys
fmt.Println(slices.Equal(gotVals, WantValsAsc))
sort.Sort(sort.Reverse(sort.StringSlice(gotVals))) //降序排列
//gotKeys 是否等于wantValsDesc
fmt.Println(slices.Equal(gotVals, WantValsDesc))
// 打印结果
//真的
//真的
//真的
}
func ContainsDome() {
floatSlice := []float64{2.3, 1.2, 0.2, 51.2}
// floatSlice 是否包含 2.3
fmt.Println(slices.Contains(floatSlice, 2.3))
stringSlice := []string{"z", "a", "b"}
fmt.Println(slices.Contains(stringSlice, "c"))
intSlice := []int{0, 3, 2, 1, 6}
fmt.Println(slices.Contains(intSlice, 0))
// 打印结果
//真的
//错误的
//真的
}
func ContainsFuncDome() {
floatSlice := []float64{2.3, 1.2, 0.2, 51.2}
// floatSlice 是否包含 > 1.0fmt.Println(slices.ContainsFunc(floatSlice, func(v float64) bool {
返回 v > 1.0
}))
stringSlice := []string{"z", "a", "b"}
// stringSlice 是否包含 == "c"
fmt.Println(slices.ContainsFunc(stringSlice, func(v string) bool {
返回 v == "c"
}))
intSlice := []int{0, 3, 2, 1, 6}
// intSlice 是否包含 v%2 == 0
fmt.Println(slices.ContainsFunc(intSlice, func(v int) bool {
返回 v%2 == 0
}))
// 打印结果
//真的
//错误的
//真的
}
函数 InsertDome() {
我 := []int{1, 2, 3}
//在i中第一个角位置插入4 5
gotInt := slices.Insert(i, 1, []int{4, 5}...)
fmt.Println(gotInt)
//在f中第一个角位置插入4.1,5.6
f := []float64{1.2, 2.2, 3.3}
gotFloat64 := slices.Insert(f, 1, []float64{4.1, 5.6}...)
fmt.Println(gotFloat64)
s := []字符串{"a", "b", "c"}
//在s中第一个下标位置插入“e”、“f”
gotString := slices.Insert(s, 1, []string{"e", "f"}...)
fmt.Println(gotString)
// 打印结果
//[1 4 5 2 3]
//[1.2 4.1 5.6 2.2 3.3]
//[a e f b c]
}

更多介绍:
Go1.18新特性——泛型
https://www.sychzs.cn/aganippe/p/16014701.html

3。声明通用地图

包装主
导入“fmt”
函数主() {
测试演示01()
}
函数 testDemo01() {
type M[K string, V any] map[K]V //这里的K不支持any ,由于底层map不支持,所以使用string
m1 := M[string, int]{"key": 1}
m1["key"] = 2
m2 := M[string, string]{"key": "value"}
m2["key"] = "new value"
fmt.Println(m1, m2)
//打印
//map[key:2] map[key:new value]
}

4. 声明一个泛型通道

package main
import "fmt"
type C[T any] chan T
func main() {
c1 := make(C[int], 10)
c1 <- 1
c1 <- 2
c2 := make(C[string], 10)
c2 <- "hello"
c2 <- "world"
fmt.Println(<-c1, <-c2)
//打印
//1 hello
}

5. 泛型约束

5.1 使用interface中规定的类型约束泛型函数的参数

NumStr,新增了类型列表表达式,它是对类型参数进行约束。
使用 | 表示取并集
如果传入参数不在集合限制范围内,就会报错。

package main
import "fmt"
type NumStr interface {
Num | Str
}
type Num interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~complex64 | ~complex128
}
type Str interface {
~string
}
func add[T NumStr](a, b T) T {
return a + b
}
//使用interface中规定的类型约束泛型函数的参数
func main() {
fmt.Println(add(3, 4))
fmt.Println(add("hello", "world"))
//打印
//7
//helloworld
}

5.2 使用interface中规定的方法来约束泛型的参数

package main
import (
"fmt"
"strconv"
)
type ShowPrice interface {
String() string
}
type Price int
func (i Price) String() string {
return strconv.Itoa(int(i))
}
type Price2 string
func (i Price2) String() string {
return string(i)
}
func ShowPriceList[T ShowPrice](s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return
}
//使用interface中规定的方法来约束泛型的参数
func main() {
fmt.Printf("%T %+v \n", ShowPriceList([]Price{1, 2}), ShowPriceList([]Price{1, 2}))
fmt.Printf("%T %+v \n", ShowPriceList([]Price2{"a", "b"}), ShowPriceList([]Price2{"a", "b"}))
//打印
//[]string [1 2]
//[]string [a b]
}

5.3 使用interface中规定的方法和类型来双重约束泛型的参数

package main
import (
"fmt"
"strconv"
)
type ShowPrice interface {
String() string
int | string
}
type Price int
func (i Price) String() string {
return strconv.Itoa(int(i))
}
func ShowPriceList[T ShowPrice](s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return
}
//使用interface中规定的方法和类型来双重约束泛型的参数
func main() {
fmt.Printf("%T %+v", ShowPriceList([]Price{1, 2}), ShowPriceList([]Price{1, 2}))
}
//传入浮点参数,就会因为不是约束类型而报错
// .\main.go:27:36: Price does not implement ShowPrice (possibly missing ~ for int in constraint ShowPrice)

5.4 使用泛型自带comparable约束,判断比较

package main
import (
"fmt"
)
func findFunc[T comparable](a []T, v T) int {
for i, e := range a {
if e == v {
return i
}
}
return -1
}
func main() {
fmt.Println(findFunc([]int{1, 2, 3, 4, 5, 6}, 5))
fmt.Println(findFunc([]string{"烤鸡", "烤鸭", "烤鱼", "烤面筋"}, "烤面筋"))
// 打印
// 4
// 3
}

comparable 的约束类型支持整数 和字符,自定义结构体,也可以嵌套在自定义约束中

type ShowPrice interface {
int | string | comparable
}

6.声明一个泛型struct

package main
import (
"fmt"
"www.sychzs.cn/x/exp/constraints"
)
type Vector[T constraints.Ordered] struct {
x, y T
}
func (v *Vector[T]) Add(x, y T) {
v.x += x
v.y += y
}
func (v *Vector[T]) String() string {
return fmt.Sprintf("{x: %v, y: %v}", v.x, v.y)
}
func NewVector[T constraints.Ordered](x, y T) *Vector[T] {
return &Vector[T]{x: x, y: y}
}
func main() {
v := NewVector[float64](1, 2)
v.Add(2, 3)
fmt.Println(v)
v2 := NewVector[string]("a", "b")
v2.Add("1", "2")
fmt.Println(v2)
//打印:
//{x: 3, y: 5}
//{x: a1, y: b2}
}

参考文档:
http://www.sychzs.cn/article/193584
https://www.sychzs.cn/QcloudCommunity/article/details/121219750
https://www.sychzs.cn/db/528594

视频教程:
https://www.sychzs.cn/video/BV1PY41137Vn?p=2&vd_source=a68414cd60fe26e829ce1cdd4d75a9e6

相关文章