Go 最受期待的功能之一是泛型功能,在最新版本 Go 1.18 中正式添加。在本文中,我们将讨论泛型是什么,为什么它们很重要以及如何在 Go 编程中使用它们。
什么是泛型?
泛型是一种编程功能,允许您编写能够处理任何类型值的函数和数据类型。换句话说,泛型是一种编写代码的方式,它足够灵活,可以处理不同类型的数据,而无需重复代码。泛型通常用于 C++、Java 和 Python 等编程语言中。
泛型为什么重要?
泛型非常重要,因为它们有助于减少代码重复并增加代码可重用性。在泛型出现之前,开发人员必须为他们想要处理的每种数据类型编写单独的函数或数据类型。这可能导致大量重复的代码,并使维护和调试代码更加困难。有了泛型,开发人员可以编写一个可以与任何类型的数据一起使用的单个函数或数据类型,使他们的代码更加高效并易于阅读。
如何在 Go 中使用泛型
在 Go 中,泛型是使用类型参数实现的。类型参数是在调用函数或数据类型时指定的类型的占位符。要在 Go 中使用泛型,您需要使用语法“type T”定义类型参数。T 可以替换为任何有效的 Go 标识符。
一旦您定义了类型参数,就可以在您的代码中使用它来替换特定类型。例如,如果您想编写一个可以处理任何类型数据的函数,您可以像这样定义一个类型参数:
// Cond Ternary Operator
func Cond[T any](condition bool, a, b T) T {
if condition {
return a
}
return b
}
// print: right
fmt.Println(gogeneric.Cond(78 > 60, "right", "wrong"))
any是新的关键字,等同于interface{}, 用于指定类型参数可以是任何类型。如果你想限制可以与函数一起使用的类型,你可以用一个特定的类型或类型列表来替换 “any”。
这个例子实现了一个简单的三元运算,三个参数分别是表达式,成立时的结果,不成立时的结果,由于参数a和b的类型都是T,所以他们必须是相同类型的,这个例子中使用这个泛型函数不需要指定T的类型为string,是因为可以根据你传递的参数进行类型判断。
官方代码示例
// MapKeys returns a slice of all the keys in m.
// The keys are not returned in any particular order.
func MapKeys[Key comparable, Val any](m map[Key]Val) []Key {
s := make([]Key, 0, len(m))
for k := range m {
s = append(s, k)
}
return s
}
这个函数实现了把一个map转换为slice的功能,把map中的key拼接成一个slice返回。如果是没有泛型之前,这个需求该如何实现呢?
func StringMapKeys(m map[string]string) []string {
s := make([]string, 0, len(m))
for k := range m {
s = append(s, k)
}
return s
}
假如参数map固定为map[string]string类型,那么上述方法也很好地完成了需求,但如果map的类型是不固定的,可能是map[string]int/map[int]int,那么为了能实现同样的需求可能要写好几个代码近乎一样的函数,而泛型函数,可以解决这个痛点。
这个泛型例子中comparable代表可比较类型,是一个接口,也是新引进的预定义标识符,仅能用于泛型中的类型指定,any是任意类型,所以这个泛型可以匹配任何类型的map。
类型范围约束
// Number constraint normal number type
type Number interface {
~uint | ~uint32 | ~uint64 | ~int | ~int32 | ~int64 | ~float32 | ~float64
}
// Min returns the minimum value
func Min[T Number](base, comp T) T {
res := base
if base > comp {
res = comp
}
return res
}
上面的例子中,大多数都用any类型,也就是任意类型来约束泛型的参数。我们同样可以指定更小范围的类型,比如comparable代表可比较类型,在上述代码中,我们定义了一个”Number”类型的类型约束,声明了这个约束中有哪些类型。
~uint约束代表底层是uint的类型也可以接受,比如使用type MyUint uint,此时MyUint类型也符合Number类型的约束。如果直接使用uint约束,则不满足。
Min函数实现了一个很简单的需求:比大小,但是限定了参数的类型返回必须是在Number中定义的,我们可以轻松使用这个函数比较各种类型的变量大小,在游戏中编程中可能会很好用。
何时使用泛型
个人使用go泛型也比较久了,主要是用于一些工具函数。当你实现同一需求但是因为参数类型的不同而需要重复写一些相同的代码或者使用泛型/interface{}时,泛型可以帮助你抽象出更快捷安全的函数/方法。
结论
泛型是一种强大的功能,可以使您的代码更加高效并易于阅读。随着泛型的添加到 Go 中,开发人员现在拥有更多的工具来编写灵活和可重用的代码。如果您是 Go 的新手,我鼓励您了解更多关于泛型以及如何在您的编程项目中使用它们。