avatar🌌
TangentTangent Blog

技术博客 | Go & Kubernetes & Linux

技术博客 | Go & Kubernetes & Linux

Go 语言泛型使用教程

Go 语言泛型通过引入类型参数和约束,使得代码可以适用于多种类型,从而提高了代码的重用性和类型安全性。

1. 泛型类型参数和内置约束 ​

在定义泛型函数或结构体时,类型参数被放置在方括号 [] 中。Go 提供了几个重要的内置约束:

约束名称描述示例用法来源
any类型参数可以是任何类型。用于链表节点 element[T any]。
comparable类型参数必须支持等值比较(如 == 或 !=)。用于 map 的键类型 K。
cmp.Ordered类型参数必须支持顺序比较(如 <, >),常用于排序和最小值计算。用于比较函数 GMin[T cmp.Ordered]。

1.1 使用内置约束的泛型函数 ​

GMin 函数 (查找最小值)

使用 cmp.Ordered 约束,确保类型 T 支持 < 运算符:

go
func GMin[T cmp.Ordered](x, y T) T {
    if x < y {
        return x
    }
    return y
}
// 示例:GMin(1, 2)

Keys 函数 (获取 map 的键)

使用 comparable 约束 K 作为 map 的键类型,并使用 any 约束 V 作为 map 的值类型:

go
func Keys[K comparable, V any](m map[K]V) []K {
    r := make([]K, 0, len(m))
    for k := range m {
        r = append(r, k)
    }
    return r
}

2. 泛型数据结构:单向链表 List ​

泛型结构体允许创建可存储不同类型数据的通用容器。

2.1 结构体定义 ​

List 结构体和其节点 element 都使用 [T any] 作为类型参数,表示它们可以存储任何类型的值。

go
// element 是链表中的一个节点,它也是一个泛型类型。
type element[T any] struct {
    val T
    next *element[T]
}

// List 是一个泛型单向链表。它可以存储任何类型的值。
type List[T any] struct {
    head, tail *element[T]
    length int
}

2.2 泛型方法实现 ​

Push 方法(添加元素至尾部)

Push 方法接收类型参数 T 的值 v。

go
func (l *List[T]) Push(v T) {
    e := &element[T]{val: v}
    if l.tail != nil {
        l.tail.next = e
        l.tail = e
    } else {
        l.head = e
        l.tail = e
    }
    l.length++
}

Pop 方法(移除尾部元素)

Pop 方法返回类型参数 T 的值 v。该方法需要处理链表为空、只有一个元素以及多个元素的情况。

go
func (l *List[T]) Pop() (v T, ok bool) {
    if l.head == nil { // 链表为空
        return
    }
    
    if l.head == l.tail { // 只有一个元素
        v = l.head.val
        l.head = nil
        l.tail = nil
    } else {
        p := l.head
        for p.next != l.tail { // 找到倒数第二个节点
            p = p.next
        }
        v = l.tail.val
        p.next = nil
        l.tail = p
    }

    l.length--
    ok = true
    return
}

3. 泛型函数:Map 和 Filter ​

泛型函数可以实现常见的集合操作,适用于不同类型的切片。

3.1 Filter 函数 ​

Filter 函数接受一个类型为 T 的切片,并根据一个返回布尔值的 predicate 函数来过滤元素,返回满足条件的元素的新切片。

go
func Filter[T any](collection []T, predicate func(item T, index int) bool) []T {
    result := make([]T, 0, len(collection))
    for i, item := range collection {
        if predicate(item, i) {
            result = append(result, item)
        }
    }
    return result
}

3.2 Map 函数 ​

Map 函数使用两个类型参数 T 和 U,将一个类型为 T 的切片通过 iteratee 函数转换为一个类型为 U 的新切片。

go
func Map[T any, U any](collection []T, iteratee func(item T) U) []U {
    result := make([]U, len(collection))
    for i, item := range collection {
        result[i] = iteratee(item)
    }
    return result
}

4. 自定义约束(Interface Constraints) ​

当内置约束无法满足需求时,可以使用接口来定义自定义约束,限定类型参数必须满足的类型集合或底层结构。

4.1 联合类型约束 (Union Types) ​

联合类型约束使用 | 运算符,将多个具体类型组合起来,限制类型参数只能是这些类型中的一个。

示例:API 响应数据约束 (APIData)

  1. 定义数据结构:定义 User 和 Action 结构。

    go
    type User struct { ... } //
    type Action struct { ... } //
  2. 定义联合接口:APIData 约束类型参数 T 只能是 User 或 Action。

    go
    type APIData interface {
        User | Action
    } //
  3. 定义泛型结构体:APIResponse 使用 APIData 约束类型参数 T。

    go
    type APIResponse[T APIData] struct {
        Code int    `json:"code"` //
        Msg string `json:"msg"` //
        Data T      `json:"data"` //
    }

使用示例:在解析 API 响应时,通过指定具体类型(如 APIResponse[User] 或 APIResponse[Action]),可以确保 Data 字段的类型安全。

4.2 底层类型约束 (Underlying Type Constraint) ​

使用 ~ 符号可以定义底层类型约束,允许类型参数匹配所有具有特定底层类型的类型(包括基础类型和自定义类型)。

示例:整数类类型约束 (IntLike)

  1. 定义底层接口:IntLike 匹配底层类型为 int 的所有类型。

    go
    type IntLike interface {
        ~int
    } //
  2. 定义自定义类型:基于 int 定义 MyInt 和 YourInt。

    go
    type MyInt int //
    type YourInt MyInt //
  3. 定义泛型函数:Add 函数使用 IntLike 约束。

    go
    func Add[T IntLike](a, b T) T {
        return a + b
    } //

使用示例:Add 函数可以接受内置 int 类型,也可以接受自定义类型 MyInt 和 YourInt 的值。

go
var a MyInt = 10
var b MyInt = 20
// MyInt 相加: 30
Add(a, b) //
Kubernetes Secret 详解
KEDA 缩容为 0(Scale to Zero)功能概述
由 Valaxy v1.2.2 驱动|主题-Yunv1.2.2