Golang的泛型讲解
一. 泛型的定义
泛型即开发过程中编写适用于所有类型的模板, 只有在具体使用的时候才能确定其真正的类型
二. 泛型的引入与声明
以下是一段求两个int类型最大值的代码
func MaxValue(a, b int) int {
if a > b {
return a
}
return b
}
我们发现如果想求float64的最大值, 就需要再复制粘贴一份重合度极大的代码, 降低了代码的复用性
这个时候就需要泛型, 我们可以定义一下函数
func MaxValue[T int | float64](a, b T) T {
if a > b {
return a
}
return b
}
这样的话我们就可以同时完成两个浮点数或者两个int类型的取最大值的函数
通过该案例, 我们也知道了泛型函数编写的基本语法
func 函数名[T 参数约束](参数列表) 返回值 {
}
一般参数约束我们会传入一个接口类型作为约束, 以防指针的*产生歧义, 提高编译速度
func function[T interface{*int | *float64}](a, b T) {}
回到以上求Max的函数, 如果我们需要获取任意类型的最大值, 该怎么写呢?
这里go语言提供了any 关键字, 代表任何类型, 其实也就是相当于空接口interface{}
func MaxValue[T any](a, b T) T {
if a > b {
return a
}
return b
}
但是这样会报错, 因为任何类型并不是都支持比较, 那么我们想让任何支持比较类型的都传入该怎么办呢?
go1.21 新加了内置函数min和max, 我们不妨进入源码看看设计者是怎么写的
func max[T cmp.Ordered](x T, y ...T) T
我们发现这里的参数约束是一个cmp.Ordered接口, 通过字面意思我们就可以猜出, 这里面应该是一些可以比较的参数类型
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}
其中~ 代表其类型以及所有的衍生类型
类型集合
这里我们另外提出类型集合的概念, 类型集合其实就是一个特殊的接口
对于一个普通的接口, 接口的元素一共有两种, 方法签名和其他接口, 但是如果作为参数约束的类型集合, 我们还可以添加额外的三种元素, 这个时候他将不能作为普通接口来使用
- 增加类型元素
- 增加近似约束元素, 也就是
~修饰的元素 - 增加联合约束, 以
|将各种类型元素或者联合类型元素连接, 代表取并集
内置类型
go提供了两个内置类型, 一个是any, 代表空接口, 一个是comparable, 支持== 或者 != 两个操作
三. 泛型的使用
1. 集合转列表
func mapToList[K comparable, V any](mp map[K]V) []V {
list := make([]V, len(mp))
var i int = 0
for _, v := range mp {
list[i] = v
i++
}
return list
}
2. 泛型接口
type GetKey[T comparable] interface {
Get() T
}
3. 泛型结构体
type MyStruct[T interface{*int | *float64}] struct {
Name string
Data T
}
4. 泛型receiver
func (mystruct MyStruct[T]) GetData() T {
}
示例
type MyStruct[T interface{*int | *float64}] struct {
Name string
Data T
}
func (mystruct MyStruct[T]) GetData() T {
return mystruct.Data
}
func main() {
data := 18
myStruct := MyStruct[*int]{
Name: "john",
Data: &data,
}
data1 := myStruct.GetData()
fmt.Println(*data1)
}
四. 泛型限制
- 匿名结构体与匿名函数不支持泛型
- 不支持类型断言
- 不支持泛型方法, 只支持通过
receiver来实现方法的泛型处理 ~后的类型必须为基本类型, 不能为接口类型

浙公网安备 33010602011771号