go1.18终于发布了,据说支持了泛型,今天让我来一探究竟。
安装
执行如下命令
brew install go
确认安装完成
192:bin mason$ go version
go version go1.18 darwin/amd64
泛型讲解
先看个问题
在日常开发中我们经常会遇到判断数组是否包含某一元素的场景,举个例子:
1.判断int64数组中是否包含指定值
2.判断int32数组中是否包含指定值
3.判断string数组中是否包含指定值
4....
针对这种场景,通常我们会封装一些通用的方法,但由于go是强类型语言,我们只能针对每种类型单独封装一个方法,如下:
func ContainInt64(target int64, arr []int64) bool {
for _,v := range arr {
if v == target {
return true
}
}
return false
}
func ContainInt32(target int32, arr []int32) bool {
for _,v := range arr {
if v == target {
return true
}
}
return false
}
func ContainString(target string, arr []string) bool {
for _,v := range arr {
if v == target {
return true
}
}
return false
}
上述方法除类型外,实现逻辑是一样的,会导致存在大量重复代码。那有什么方法能避免这个问题呢?你可能会想到反射,判断输入值的类型然后做相应的判断,这样其实把类型判断放到了函数内部,可维护性和可读性依然会比较差。我们需要的其实是一个function能支持多种不同类型,而泛型的出现正好解决了这一问题。
什么是泛型
泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型,如C++中的模板。
go的泛型
go的泛型包括了泛型函数和泛型类型两部分。借助一个示例来说下什么是泛型函数,我们回到开头说的判断数组是否包含某元素的例子,使用泛型该如何处理呢?示例如下:
func main() {
fmt.Println(ContainElement(1, []int32{1,2,3}))
fmt.Println(ContainElement("1", []string{"a","1","b"}))
fmt.Println(ContainElement("1", []string{"a","c","b"}))
}
func ContainElement[T int64 | int32 | string](target T, arr []T) bool {
for _,v := range arr {
if v == target {
return true
}
}
return false
}
//输出
true
true
false
可以看到相较于之前的函数写法,多了参数列表[T int64 | int32 | string],参数列表包括参数名(T)和约束(int64 | int32 | string)两部分,其中|表示取并集,即T可为int64、int32和string三种类型中的任意一种。
当类型较多时,约束会比较长,且多个泛型函数之间可能会使用相同的约束,此时我们可以对约束类型进行简化,如下:
func main() {
fmt.Println(ContainElement(1, []int32{1,2,3}))
fmt.Println(ContainElement("1", []string{"a","1","b"}))
fmt.Println(ContainElement("1", []string{"a","c","b"}))
fmt.Println(Sum(1,2))
fmt.Println(Sum("hello ","world"))
}
type commonType interface {
int | int64 | int32 | string
}
func ContainElement[T commonType](target T, arr []T) bool {
for _,v := range arr {
if v == target {
return true
}
}
return false
}
func Sum[T commonType](a T, b T) T {
return a + b
}
//输出
true
true
false
3
hello world
写到这儿泛型函数的使用方法大家基本都清楚了吧,golang还有一种泛型类型,大概意思就是对类型进行泛型并支持定义相关的类型函数,举个简单例子吧
type Vector[T int|string] []T
func (v *Vector[T])push (x T) {
*v = append(*v,x)
}
func (v *Vector[T]) Add () T {
var res T
for _,v1 := range *v {
res += v1
}
return res
}
func main() {
var strs Vector[string]
strs.push("aaa,")
strs.push("bbb,")
strs.push("ccc")
fmt.Println(strs.Add())
var ints Vector[int]
ints.push(1)
ints.push(2)
ints.push(3)
fmt.Println(ints.Add())
}
//输出
aaa,bbb,ccc
6
性能
- 由于支持泛型,go1.18预计比go.1.17编译速度慢15%,运行时间不受影响,目前计划在 Go1.19中提高编译器的速度。
- 20% 性能提升:对于使用Apple M1、ARM64 和 PowerPC64 用户,由于 Go 1.17 的寄存器 ABI 调用约定扩展到这些架构,Go 1.18 包括高达 20% 的 CPU 性能改进。
最后
时间有限,先写到这儿吧。最后总结一下,什么情况下用泛型呢?我认为只要是参数类型不同而操作行为相同的场景都可以使用泛型进行重构。
网友评论