泛型在 Go 1.18 版本加入,您需要开始使用 1.18 版本,您可以在此处下载。

什么是泛型?为什么 Go 需要它?

abab
intint64float
intfloat

image.png

floatint

因此,唯一合适的解决方案是编写两个有重复功能的函数,如下所示。

Add(a,b int) intAddFloat(a,b float32) float32
interface{}interface

这就是泛型解决的问题,也是为什么这么多开发人员非常渴望看到它发布的原因。

泛型在 Go 1.18 发布后,上述问题的解决方案变得很简单。您可以点击此处获取可运行的代码。

image.png

泛型的工作原理

让我们深入了解泛型的基本用法。

Add
func FunctionName
(a int)a
func Add(a, b int) int {
  return a + b
}
复制代码
Addabint

解释函数参数看起来是愚蠢的,但是了解泛型之前,先了解他它是至关重要的。

[][](a,b int)

类型参数的参数名通常大写,使它们更容易被发现。

V[V int]
func Add[V int](a, b int) int {
  return a + b
}
复制代码
VVV
abVV
func Add[V int](a, b V) V {
  return a + b
}
复制代码
intint
floatAddfloat32 does not implement intAddint|orV
func Add[V int | float32](a, b V) V {
  return a + b
}
复制代码
AddAdd

image.png

在您成为泛型高手之前的最后一件事。您可以在调用的泛型函数前设置要返回的参数类型。如下所示。

result := Add[int](a, b)
复制代码
~

我们现在可以使用泛型函数了,但是还剩下一些细节。想象一下,如果您想控制在泛型函数中返回的数据类型应该怎么办?

Add[int](1,2) // 希望返回 int 数据类型
Add[int64](1,2) // 希望返回 int64 数据类型
复制代码

我们现在可以告诉函数返回什么数据类型,太棒了!

Addalias

您将无法告诉函数返回什么类型的数据。

// 创建一个自定义类型 OwnInteger
type OwnInteger int

var myInt OwnInteger
myInt = 10
Add(myInt, 20) // 这将引发 painc,因为 myInt 不属于任何类型
复制代码
~

image.png

通过泛型完成类型约束

generic functionsgeneric types
ResultsAddable
type Results[T Addable] []T
复制代码
SliceAddableResultsAddable

image.png

Addable
var resultStorage Results[Addable]
复制代码
Addableinterface contains type constraints
anyResultsanyinterface{}

image.png

通过泛型完成接口约束

目前为止,我们只为单个类型进行约束,但是泛型也可以用作接口的约束。

Move
type Moveable interface {
   Move(int)
}

// Move 是一个泛型函数,它接收 Moveable 并对其进行移动
func Move[V Moveable](v V, meters int) {
   v.Move(meters)
}
复制代码
PersonCar

image.png

到目前为止,我们只使用了一个通用参数来保持简单。但请记住,您可以添加多个,并输出多个。

MoveableSubtractableMoveDistance
func Move[V Moveable, S Subtractable](v V, distance S, meters S) S
复制代码
panicMoveintSubtractable

image.png

Subtractable

不过,有一种方法可以得到我们想要的,那就是泛型结构体。

Moveable
// 实现这个接口,需要一个具有 Subtractable 约束的泛型类型
type Moveable[S Subtractable] interface {
    Move(S)
}
复制代码
[]
type Car[S Subtractable] struct {
   Name string
}

type Person[S Subtractable] struct {
   Name string
}

func main(){
   p := Person[float64]{Name: "Guowei"}
   c := Car[int]{Name: "POLO"}
}
复制代码

我们结构体实现接口的方法也需要修改。

func (p Person[S]) Move(meters S) {
   fmt.Printf("%s moved %d meters\n", p.Name, meters)
}

func (c Car[S]) Move(meters S) {
   fmt.Printf("%s moved %d meters\n", c.Name, meters)
}
复制代码
MoveMoveable
func Move[V Moveable[S], S Subtractable](v V, distance S, meters S) S {
   v.Move(meters)
   return Subtract(distance, meters)
}
复制代码
mainCarintPersonfloat64Movefloat64Moveint
func main(){
   p := Person[float64]{Name: "GuoWei"}
   c := Car[int]{Name: "POLO"}
   
   milesToDestination := 100
   distanceLeft := Move(c, milesToDestination, 95)
   fmt.Println(distanceLeft)
   fmt.Println("DistanceType: ", reflect.TypeOf(distanceLeft))

   newDistanceLeft := Move[Person[float64], float64](p, float64(distanceLeft), 5)
   fmt.Println(newDistanceLeft)
   fmt.Println("DistanceType: ", reflect.TypeOf(newDistanceLeft))
}
复制代码

您可以点击此处获取可运行的代码。

MoveMove[Person[float64], float64]
func Move[S Subtractable, V Moveable[S]](v V, distance S, meters S) S {
   v.Move(meters)
   return Subtract(distance, meters)
}
newDistanceLeft := Move[float64](p, float64(distanceLeft), 5)
复制代码

总结

Go
Go1.18

其他

封面来自互联网。