深入浅出Go泛型
Go是一门静态类型的语言,静态类型也就意味着在使用Go语言编程时,所有的变量、函数参数都需要指定具体的类型,同时在编译阶段编译器也会对指定的数据类型进行校验。这也意味着一个函数的输入参数和返回参数都必须要和具体的类型强相关,不能被不同类型的数据结构所复用。
而泛型就是要解决代码复用和编译期间类型安全检查的问题而生的。泛型是静态语言中的一种编程方式,可以让算法不再依赖于某个具体的数据类型,而是通过将数据类型进行参数化,以达到算法可复用的目的。Go V1.18开始支持泛型。(接口 interface{} 实现类似泛型的能力,但需要在运行时进行断言判断,还有类型转换的开销,代码上也不够优雅)
泛型: 属于静态语言中的一种编程方式,可以让算法不再依赖于某个具体的数据类型,而是通过将数据类型进行参数化,以达到算法可复用的目的。【只有在具体使用的时候才能确定真正的类型,解决代码复用和编译期间类型安全检查的问题】
Go语言中的泛型是通过参数化类型实现的。类型参数定义在函数或类型(如结构体、接口、切片等)之上,允许在声明时不指定具体的类型,而是在使用时指定。
泛型函数
假设我们想要写一个计算两个数之和的函数:
func Add(a int, b int) int {
return a + b
}
这个函数理所当然只能计算int的和,而浮点的计算是不支持的。这时可以像下面这样定义一个泛型函数:
func Add[T int | float32 | float64](a T, b T) T {
return a + b
}
上面就是泛型函数的定义,这种带类型形参的函数被称为泛型函数。
它和普通函数的点不同在于函数名之后带了类型形参。
和泛型类型一样,泛型函数也是不能直接调用的,要使用泛型函数的话必须传入类型实参之后才能调用。
注意:
1)匿名函数不支持泛型,因为匿名函数不能自己定义类型形参。
2)支持泛型函数,目前但不支持泛型方法
type A struct {
}
// 不支持泛型方法
func (receiver A) Add[T int | float32 | float64](a T, b T) T {
return a + b
}
但因为receiver支持泛型, 所以如果想在方法中使用泛型的话,目前唯一的办法就是曲线救国,迂回地通过receiver使用类型形参:
type A[T int | float32 | float64] struct {
}
// 方法可以使用类型定义中的形参 T
func (receiver A[T]) Add(a T, b T) T {
return a + b
}
// 用法:
var a A[int]
a.Add(1, 2)
var aa A[float32]
aa.Add(1.0, 2.0)
如果经常要分别为不同的类型写完全相同逻辑的代码,那使用泛型将是最合适的选择。
泛型的作用与应用场景:
1)增加代码的复用,从同类型的复用到不同类型的代码复用
2)应用于不同类型间代码复用的场景,即不同类型需要写相同的处理逻辑时,最适合用泛型
泛型的利弊:
1)提高了代码复用率,提高了编程效率
2)不同类型间代码复用,使代码风格更加优雅
3)增加了编译器的负担,降低了编译效率
总结:
泛型并不取代Go1.18之前用接口+反射实现的动态类型,在下面情景的时候非常适合使用泛型:当需要针对不同类型书写同样的逻辑,使用泛型来简化代码是最好的 (比如想写个队列,写个链表、栈、堆之类的数据结构)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」