Go 在 1.18 版本中加入了泛型,这或许是很多跨语言同学非常期待的事情。
这篇文章就带大家快速的学习下,Go 语言中的泛型是如何使用的。
一、一些场景
假如我们需要打印一个数组,他可以是任何类型的数组,我们可能是这样写的:
func PrintArray(arr []interface{}) {
for _, a := range arr {
fmt.Println(a)
}
}
但是好像调用时不方便:
users := []string{"zhangSan", "lisi"}
PrintArray(users) //不兼容,报错
虽然是 interface 类型的数组,但是却不能兼容 []string 类型。
在没出现泛型的时候,我们只能改入参参数:
func PrintArray(arr interface{}) {
for _, a := range arr.([]string) {
fmt.Println(a)
}
}
我们就需要去预判会传入什么类型,然后做什么处理。
二、泛型的引入
如果引入泛型之后,这样的场景就方便多了。
思路是这样的:我们不限定他类型,让调用者自己去定义类型。
于是就改成这样:
func PrintArray[T any](arr []T) {
for _, a := range arr {
fmt.Println(a)
}
}
PrintArray 后面的 [T any] 就是预留泛型,any 表示可以是任何类型。
最后返回的值,类型也和传入的类型一样。
然后我们就可以随便的传值了:
users := []string{"zhangSan", "lisi"}
PrintArray(users)
是不是一下代码就简洁多了。
三、实体类中的使用
其实上面那个 demo 还不是特别明显,我们现在把他用到 struct 里面就会更加体会深刻。
我们说下场景:
因为某些历史原因,我们的用户 Id 可能是数字,也可能是雪花 Id,所以我们的结构体就可以这样定义:
type UserModel[T any] struct {
Id T //可能是数字也可能是字符串
Name string
}
那我们在使用这个结构体的时候就的是这样:
u1 := &UserModel[int]{Id: 123, Name: "ls"} //初始化时才指定类型
u2 := &UserModel[string]{Id: "one", Name: "zs"}
fmt.Println(u1, u2)
你会发现,被我们加入了泛型的结构体的 Id 类型,在我们初始化这个结构体时才被定义类型。
泛型的语法就是这样的!
三、方法里面使用泛型
最后我们来看下在我们的方法里面怎么去使用泛型?
UserModel
// NewUserModel 构造函数是需要指定类型
func NewUserModel[T any](id T) *UserModel[T] {
return &UserModel[T]{Id: id}
}
这里我们需要注意两处做泛型的声明即可:
[T any][T any, T2 any](id T)*UserModel[T]
四、总结
对于泛型,不同的人有不同的看法,有些人认为 Go 他不需要泛型,会造成 Go 变得复杂。
也有人认为,需要用到的泛型,毕竟他是高级语言,别的语言,比如 java 都有,Go 也不能少。
我也不想去站队,毕竟那是语言的创造者的事,官方出,我就用,不出,也不期待。
但是任何一门编程语言的发展都是从最开始的简洁,慢慢发展到后面的冗余,这貌似就是一个魔咒。