我们知道,Go 最开始是不支持泛型的,最近版本(1.18)才开始支持。在支持泛型之前,一些夸类型的操作不得不通过反射来实现。那如何在 Go 中来使用泛型呢,我们可以跟我们熟悉的 TS 来做一个比较。
泛型的基础概念
-
基本的泛型函数, 如果没有泛型,我们就需要分别定义多个函数,或者用 any 作为类型。通过泛型,我们可以对不同类型的参数做类似的处理。比如:
gofunc minValue[T float32 | float64 | int16 | int32 | int64 | int](a T, b T) T { if a < b { return a } return b } minValue[int](1, 2)
-
泛型的类型推断, 这里,我们在函数调用的时候制定了类型约束,实际上我们已经可以通过函数参数的上下文得知,我们的类型约束为 int, 这里可以简写成:
gominValue(1, 2)
-
定义类型约束, 在泛型的类型约束中,我们会写的比较长,是否可以进行约束的定义呢。我们可以使用 interface 关键字来进行类型约束定义:
gotype Number interface { float32 | float64 | int16 | int32 | int64 | int } func minValue[T Number](a T, b T) T { if a < b { return a } return b }
-
~ 关键字的使用, 有时候,我们会使用自定义类型, 比如:
gotype MyFloat float32 var a1 MyFloat = 0.1 var a2 MyFloat = 0.2 minValue(a1, a2)
这里会出现类型错误:
MyFloat does not satisfy Number (possibly missing ~ for float32 in Number)
其实很容易,如果需要支持自定义的类型,只需要在类型约束前加上:~
,比如:gotype Number interface { ~float32 | ~float64 | ~int16 | ~int32 | ~int64 | ~int }
泛型的进阶
-
类型约束和接口,我们知道 interface 既可以用来做类型约束,也可以用来做接口定义。某种程度上说,他们是一致的,比如:
-
我们通过约束 C1 定义泛型函数:
gotype C1 interface { ~int | ~int32 M1() } func foo[P C1](t P) { }
-
同时我们定义 T 实现接口,但是不实现类型约束:
gotype T struct{} func (T) M1() {} var t T foo(t) // 编译器报错:T does not satisfy C1 (T missing in ~int | ~int32)
-
如果我们实现了类型约束但是没有实现接口:
gotype T int var t T foo(t) // T does not satisfy C1 (missing method M1)
-
只有同时满足了类型约束和接口实现才能通过编译:
gotype T int func (T) M1() {} var t T foo(t) // 编译器报错:T does not satisfy C1 (T missing in ~int | ~int32)
-