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 新加了内置函数minmax, 我们不妨进入源码看看设计者是怎么写的

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
}

其中~ 代表其类型以及所有的衍生类型

类型集合
这里我们另外提出类型集合的概念, 类型集合其实就是一个特殊的接口
对于一个普通的接口, 接口的元素一共有两种, 方法签名和其他接口, 但是如果作为参数约束的类型集合, 我们还可以添加额外的三种元素, 这个时候他将不能作为普通接口来使用

  1. 增加类型元素
  2. 增加近似约束元素, 也就是~修饰的元素
  3. 增加联合约束, 以|将各种类型元素或者联合类型元素连接, 代表取并集

内置类型
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)
}

四. 泛型限制

  1. 匿名结构体与匿名函数不支持泛型
  2. 不支持类型断言
  3. 不支持泛型方法, 只支持通过receiver 来实现方法的泛型处理
  4. ~后的类型必须为基本类型, 不能为接口类型
posted @ 2023-12-17 17:21  Xingon2356  阅读(1320)  评论(0)    收藏  举报