请在golang 1.18后再使用泛型
什么场合需要泛型呢?
我自己的回答:其实大部分场合我们都不太需要请泛型这尊大佛。golang的接口可以应对我们的需求。我们在1.18之前,没有泛型,还不是一样的写代码。但是有了泛型之后,写一些工具类代码更清晰。golang的反射用起来还是挺麻烦的。我想,我们平时需要做各种验证吧,比如,数值在XX之间,检查数组中的数值合不合要求,这些当然用传统的go代码也可以,但是不如泛型明了。 来个超级简单的前瞻:
// 定义
func Sum[T int | float64](a, b T) T {
return a + b
}
// 调用
Sum(1, 2)
Sum(1.0, 2.0)
Sum[int](1, 2) // 纯纯的多余,因为编译器可以算出来
也许你会有以下疑问?
- 可以在结构体方法中用么?
- 类型参数可以是结构体,可以是interface么?
- 调用的参数类型可以多种混合么?类似
Sum(1,2.0)
我们先看一点小知识,后续再解答这些问题吧。
前置小知识
interface{}可以简写为anycomparable是指可比较类型,但是又不能比较,因为只支持 ==、!=- 类型参数:
[TypeName1 类型1 TypeName2 类型2]这里值得好好注意
FBI WARNING
类型参数可以是只是一组类型参数 如:[T int|string]、[Nxxx string|uint]等,第一个TypeName没有什么命名要求。第二个类型就有点讲究了。可以复合type参数,如[T int|string], 还可以是类型混结构体[T MyStruct|int], 还可以玩的很极限,没有数量限制[T uint | uint8 | uint16 | string | bool]。但是,请不要和interface混用,不是我不让,也没有什么人生经验,纯是编译器不让我们玩的这么极限。 你也可以玩的很极限,来多组类型。
实践一下
说了这么多,上货!我们先从简单的情况开始说起,然后慢慢增加使用场合。
- 普通函数的泛型
- 结构体函数的泛型
1 普通函数
普通函数的代码片段就和上文一样,但是我还是把他贴到下面了
// 定义
func Sum[T int | float64](a, b T) T {
return a + b
}
然后就是调用
Sum(1, 2)
Sum(1.0, 2.0)
Sum[int](1, 2)
Sum[float64](1.0, 2.0)
是可以不用写两组Sum分别计算int及float64了,那么问题来了,我要计算int + float64呢?就像这样Sum(1,1.0),结果肯定是不行的。
所以Sum(1.0,2)也是不行的。泛型似乎有用,但是没有那么有用。
参数类型也可以是多组,但是多组之间也不能互相直接参与计算。因为直接报错了,所以直接上图
其实也是很蛋疼的事情。那么多组还有什么用呢?有的,看官方代码,计算一个map的总和,map有两个类型么,就派上用场了。
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
也许看到这里,你会大失所望吧,就这就这?是啊,其实泛型只适合写一点通用的代码,链表啊、图啊、比比大小啊、计算总和啊,没有我们想的那么灵活。
2 结构体函数
写一个验证器,验证大小对不对
package val
type number interface {
~int | ~float64
}
type MinVaildate[T number] struct {
min T
}
func (m MinVaildate[T]) DoVaildate(value T) bool {
return m.min < value
}
有小伙伴就要喷了,啊,就判断个最小值,柠有必要搞这么复杂么? 我目前找不到什么好的例子了。而且,已经出现bug了。
先这样吧,后续再更