当前位置: 首页 > 后端技术 > PHP

Go泛型的这3个核心设计你知道吗?

时间:2023-03-29 16:31:05 PHP

大家好,我是炸鱼。Go1.18的泛型闹得沸沸扬扬,虽然之前我也写过很多文章讲泛型的一些设计和思考。但是因为之前通用的proposal还没有定稿,所以没有写完整的介绍。现在已经基本成型了,建宇就带大家一起来了解一下Go泛型。本文内容主要涉及泛型的三大概念,值得深入理解。如下:类型参数。类型约束。类型推导。类型参数类型参数,名词。不熟悉的朋友乍一看会一头雾水。通用代码是使用抽象数据类型编写的,我们称之为类型参数。当程序运行泛型代码时,类型参数被类型参数替换。也就是说,类型参数是一个通用的抽象数据类型。简单的通用示例:funcPrint(s[]T){for_,v:=ranges{fmt.Println(v)}}代码中有一个Print函数,可以打印出段的每个元素,其中段中的元素的类型,这里称为T,是未知的。这里涉及到泛型语法设计的一个要点,即:T的泛型类型参数应该如何定义?在现有的设计中,分为两部分:类型参数列表:类型参数列表会出现在常规参数之前。为了区分类型参数列表和常规参数列表,类型参数列表使用方括号而不是圆括号。类型参数约束:就像常规参数有类型一样,类型参数也有元类型,称为约束(稍后会详细介绍)。一个完整的例子如下://Print可以打印任意片段的元素。//Print有一个类型参数T和一个(非类型)s,它是该类型参数的一个片段。funcPrint[Tany](s[]T){//dosomething...}在上面的代码中,我们声明了一个函数Print,它有一个类型参数T,类型约束是any,代表任意类型,功能与接口{}相同。它的输入变量s是一个T类型的slice,函数声明之后,我们需要在调用函数的时候指定类型参数的类型。如下:Print[int]([]int{1,2,3})在上面的代码中,我们指定传入的类型参数为int,将[]int{1,2,3}作为参数传入。其他的类型,比如float64:Print[float64]([]float64{0.1,0.2,0.3})也是类似的声明方式,跟着set就可以了。类型约束说完类型参数,我们再来说说“约束”。必须在所有类型参数中指定类型约束才能称为完全泛型。下面分两部分详细讲解:定义函数约束。定义操作员约束。为什么会有类型约束?为了保证调用方能够满足接收方的程序要求,保证程序中应用的函数、运算符等特性能够正常运行。通用类型参数和类型约束相辅相成。我们看一下Go官方提供的例子:funcStringify[Tany](s[]T)(ret[]string){for_,v:=ranges{ret=append(ret,v.String())//INVALID}returnret}该方法的作用是:可以将任意类型的slice转换成对应的stringslice。但是程序逻辑上有个问题,就是它的入参T是任意类型,可以传入任意类型。它内部还调用了String方法,自然会报错,因为只有像这样的类型int和float64可能不会实现此方法。你说你要定义有效的类型约束,像上面的例子,如何在泛型中实现?如果要求传入方有内置方法,则必须定义接口对其进行约束。单个类型的示例如下:typeStringerinterface{String()string}应用于泛型方法:funcStringify[TStringer](s[]T)(ret[]string){for_,v:=ranges{ret=append(ret,v.String())}returnret}然后把Stringer类型放到原来any类型的地方,就可以实现程序的要求了。如果有多个类型约束,则为多个类型。例子如下:typeStringerinterface{String()string}typePlusserinterface{Plus(string)string}funcConcatTo[SStringer,PPlusser](s[]S,p[]P)[]string{r:=make([]string,len(s))fori,v:=ranges{r[i]=p[i].Plus(v.String())}returnr}和常规输入输出类型声明相同的规则。定义运算符约束完成函数约束的定义后,剩下的大骨头就是“运算符”约束。让我们看一下Go官方的例子:funcSmallest[Tany](s[]T)T{r:=s[0]//panicifsliceisemptyfor_,v:=ranges[1:]{ifv