第33章 泛型 Generics

第33章:泛型 Generics

“泛型,这个名字听起来像是某种神秘的力量,但实际上它是Go语言在1.18版本引入的最重要的特性。有了泛型,你终于可以写一份代码,服务于多种数据类型——就像一个万能厨师,用同一套厨艺做出各种口味的菜!”

在Go语言的世界里,泛型是一个姗姗来迟的"贵客"。从1.0到1.17,Go开发者们一直眼馋其他语言的泛型特性,却只能用空接口(interface{})和类型断言来"曲线救国"。终于,在Go 1.18,这个革命性的特性正式登场了!

泛型让Go代码变得更加优雅、高效、类型安全。它不是魔法,而是一种让代码"一次编写,多种类型复用"的实用工具。


33.1 类型参数

泛型的核心就是"类型参数"。想象一下,如果你有一种"模板",可以根据不同的数据类型自动生成对应的代码,那该多方便!类型参数就是实现这种"模板"的关键。

33.1.1 类型形参

类型形参——这是泛型函数或泛型类型的"模板参数",就像建筑图纸上的预留位置,等着正式施工时填入具体材料。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 泛型函数定义
func PrintSlice[T any](s []T) {
    for _, v := range s {
        fmt.Println(v)
    }
}

//        ↑
//        T 就是类型形参
//        any 是类型约束,表示"可以是任意类型"

类型形参的"身份证解读"

1
2
3
4
5
6
func MyFunction[T any](value T) T {
    return value
}
//    ↑     ↑
//    │     └── 返回值也使用类型形参T
//    └── 类型形参T,定义在方括号里

类型形参的"书写位置"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 泛型函数
func FunctionName[T any](param T) { }

// 泛型结构体
type MyStruct[T any] struct {
    Field T
}

// 泛型接口
type MyInterface[T any] interface {
    Method(t T)
}

// 泛型方法(注意:类型形参在方法上)
type MyStruct[T any] struct {
    value T
}

func (m *MyStruct[T]) Get() T {
    return m.value
}

类型形参的命名规范

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 常见的类型形参命名:
[T any]        // 单个类型参数,最常用
[K comparable] // K表示键
[V any]        // V表示值
[K, V any]     // 多个类型参数
[T, U any]     // 多种类型

// 自定义命名也可以
[Element any]       // 元素
[Number int | float64]  // 数字

类型形参的"生命周期"

graph LR
    A["定义泛型函数"] --> B["指定类型实参"]
    B --> C["编译器生成具体代码"]
    C --> D["运行具体函数"]
    
    style A fill:#87CEEB
    style D fill:#90EE90

类型形参与普通参数的区别

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 普通函数
func DoubleInt(x int) int {
    return x * 2
}

// 泛型函数
func Double[T int | float64](x T) T {
    return x * 2
}

// 普通参数:值在运行时确定
// 类型形参:值在编译时确定

类型形参的"约束"

1
2
3
4
5
// 约束限制了类型形参可以接受哪些类型
[T any]           // 任意类型
[T comparable]    // 可比较的类型(支持==和!=)
[T int | float64] // int或float64
[T ~int]         // 以int为基础的类型(自定义int别名)

类型形参的"使用示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

// 定义泛型函数:交换两个值
func Swap[T any](a, b T) (T, T) {
    return b, a
}

func main() {
    // 使用int类型
    x, y := Swap(1, 2)
    fmt.Println(x, y)  // 2 1

    // 使用string类型
    s1, s2 := Swap("hello", "world")
    fmt.Println(s1, s2)  // world hello
}

**类型形参的"编译时处理":

graph TD
    A["源代码"] --> B["编译器处理类型形参"]
    B --> C["生成具体代码"]
    C --> D["int版本"]
    C --> E["string版本"]
    C --> F["实际运行的是具体代码"]
    
    style B fill:#FFD700
    style F fill:#90EE90

类型形参的"特点"

  1. 编译时确定:类型形参在编译时被具体类型替换
  2. 不影响运行时:泛型代码运行时没有额外开销
  3. 类型安全:编译时就检查类型是否合法
  4. 代码复用:一份代码,多种类型

小贴士:类型形参的名字只是习惯约定,你可以叫它T、K、V或者其他任何名字,但为了代码可读性,建议使用有意义的命名。

下一个小节预告:33.1.1.1 类型参数列表——泛型的"参数表"长什么样?


33.1.1.1 类型参数列表

类型参数列表——这是泛型定义的"参数表",用方括号[]包裹,告诉你这个泛型可以接受哪些"类型参数"。

1
2
3
4
5
6
7
8
9
// 单个类型参数
[T any]

// 多个类型参数
[K, V any]

// 带约束的类型参数
[T int | string]
[K comparable, V any]

类型参数列表的"标准格式"

1
2
3
4
5
6
7
8
// 单参数
[Element any]

// 多参数
[Key comparable, Value any]

// 带约束的多参数
[K int | string, V any]

类型参数列表的位置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 泛型函数:类型参数列表在函数名之后
func MyFunc[T any](param T) { }

// 泛型结构体:类型参数列表在类型名之后
type MyStruct[T any] struct { }

// 泛型接口:类型参数列表在接口名之后
type MyInterface[T any] interface { }

// 泛型方法:类型参数列表在方法名之后
func (m *MyStruct[T]) Method[V any](v V) { }

类型参数列表的"逗号与空格"

1
2
3
4
5
6
7
8
9
// 可以用逗号分隔
[T, K any]

// 也可以用空格分隔
[T any]
[K any]

// 逗号在多个类型参数时可选
[T, K, V any]  // 等价于 [T any, K any, V any]

类型参数列表与函数参数的区别

graph LR
    A["函数参数"] --> B["运行时确定"]
    A --> C["值在调用时传入"]
    
    D["类型参数"] --> E["编译时确定"]
    D --> F["类型在调用时指定"]
    
    style A fill:#87CEEB
    style D fill:#FFD700

类型参数列表的"示例代码"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import "fmt"

// 单类型参数
func Identity[T any](x T) T {
    return x
}

// 多类型参数
func Pair[K, V any](k K, v V) struct{ K K; V V } {
    return struct{ K K; V V }{K: k, V: v}
}

// 多类型参数,带约束
func AddToMap[K comparable, V any](m map[K]V, k K, v V) {
    m[k] = v
}

func main() {
    // 使用单类型参数
    n := Identity(42)
    s := Identity("hello")
    fmt.Println(n, s)  // 42 hello

    // 使用多类型参数
    p := Pair("name", "Alice")
    fmt.Println(p.K, p.V)  // name Alice
}

类型参数列表的"常见写法"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 标准库常用的类型参数命名:
[T any]         // Type,通用类型
[E any]         // Element,容器元素
[K comparable]  // Key,键
[V any]         // Value,值
[K, V any]     // 键值对
[K, V comparable]  // 可比较的键值对

// 数值计算常用:
[T int | int32 | int64 | float64 | float32]
[N int | int32 | int64 | uint | uint32 | uint64]

类型参数列表的"约束写法"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 单一约束
[T int]           // 只能是int
[T comparable]    // 必须可比较

// 联合约束
[T int | string]  // int或string

// 接口约束
[T interface{ String() string }]

// 简写
[T Stringer]      // 等价于上面

类型参数列表的"方法定义"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
type Container[T any] struct {
    elements []T
}

// 方法使用容器的类型参数
func (c *Container[T]) Get(i int) T {
    return c.elements[i]
}

// 方法可以引入新的类型参数
func (c *Container[T]) Map[U any](f func(T) U) []U {
    result := make([]U, len(c.elements))
    for i, e := range c.elements {
        result[i] = f(e)
    }
    return result
}

类型参数列表的"嵌套泛型"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 泛型嵌套
type TreeNode[T any] struct {
    value T
    left  *TreeNode[T]
    right *TreeNode[T]
}

// 泛型作为类型参数
type Pair[K, V any] struct {
    Key   K
    Value V
}

type Triple[T any] struct {
    First  T
    Second T
    Third  T
}

// 使用嵌套
type Tree[K comparable, V any] struct {
    root *TreeNode[Pair[K, V]]
}

类型参数列表的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ✅ 推荐:使用有意义的命名
[K comparable, V any]
[Element any]

// ❌ 不推荐:过于简短
[a any]
[x any]

// ✅ 推荐:约束尽量精确
[T int | float64]
[T comparable]

// ❌ 避免:约束过于宽松
[T any]  // 除非真的需要任意类型

小剧场

建筑工地:

工程师(编译器):“我收到了一份建筑图纸(类型参数列表),上面说要用K和V两种材料(类型参数)。”

施工队:“K是什么材料?”

工程师:“K是可以比较的材料(comparable),比如钢筋、混凝土。V是任意材料。让我查查(类型约束)。”

施工队:“那这个建筑(泛型结构体)既可以用钢筋建造,也可以用混凝土建造?”

工程师:“没错!这就是泛型的威力——一份图纸,多种材料!”

这就是类型参数列表的作用——告诉你这个泛型可以使用哪些"材料"(类型)!

下一个小节预告:33.1.1.2 多类型参数——一个不够,那就来两个!


33.1.1.2 多类型参数

多类型参数——当一个类型参数不够用时,泛型允许你定义多个类型参数,让代码更加灵活多变!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 单类型参数
func Identity[T any](x T) T

// 多类型参数:键值对
func GetOrSet[K comparable, V any](m map[K]V, k K, defaultVal V) V {
    if v, ok := m[k]; ok {
        return v
    }
    return defaultVal
}

多类型参数的"书写格式"

1
2
3
4
5
6
7
8
9
// 两个类型参数
[K, V any]

// 三个类型参数
[K, V, T any]

// 带约束的多类型参数
[K comparable, V any]
[T int | float64, U string]

多类型参数的实际应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import "fmt"

// Map类型:键值对
type Map[K comparable, V any] struct {
    data map[K]V
}

func NewMap[K comparable, V any]() *Map[K, V] {
    return &Map[K, V]{data: make(map[K]V)}
}

func (m *Map[K, V]) Set(k K, v V) {
    m.data[k] = v
}

func (m *Map[K, V]) Get(k K) (V, bool) {
    v, ok := m.data[k]
    return v, ok
}

func main() {
    // string作为键,int作为值
    m := NewMap[string, int]()
    m.Set("age", 25)
    m.Set("score", 100)

    age, _ := m.Get("age")
    fmt.Println(age)  // 25
}

多类型参数的"常见模式"

graph LR
    A["多类型参数的常见模式"] --> B["Pair: 键值对"]
    A --> C["Result: 结果或错误"]
    A --> D["Either: 二选一"]
    
    B --> B1["[K, V any]"]
    C --> C1["[T any, E error]"]
    D --> D1["[A, B any]"]

Pair模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Pair表示一对数据
type Pair[First any, Second any] struct {
    First  First
    Second Second
}

func NewPair[First any, Second any](f First, s Second) Pair[First, Second] {
    return Pair[First, Second]{First: f, Second: s}
}

// 使用
p := NewPair("hello", 42)
fmt.Println(p.First, p.Second)  // hello 42

Result模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Result表示操作结果
type Result[T any] struct {
    value T
    err   error
}

func Ok[T any](v T) Result[T] {
    return Result[T]{value: v, err: nil}
}

func Err[T any](e error) Result[T] {
    return Result[T]{value: *new(T), err: e}
}

func (r Result[T]) Value() (T, error) {
    return r.value, r.err
}

// 使用
result := Ok(42)
val, err := result.Value()

Either模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Either表示A或B两种类型之一
type Either[Left any, Right any] struct {
    isLeft bool
    left   Left
    right  Right
}

func Left[Left any, Right any](l Left) Either[Left, Right] {
    return Either[Left, Right]{isLeft: true, left: l}
}

func Right[Left any, Right any](r Right) Either[Left, Right] {
    return Either[Left, Right]{isLeft: false, right: r}
}

func (e Either[L, R]) IsLeft() bool {
    return e.isLeft
}

多类型参数的"类型推断"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Go可以推断类型参数
func Pair[K, V any](k K, v V) Pair[K, V] {
    return Pair[K, V]{k, v}
}

// 显式指定
p1 := Pair[int, string](1, "one")

// 类型推断(Go会自动推断)
p2 := Pair(1, "one")  // 推断为 Pair[int, string]

多类型参数的"嵌套使用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 多类型参数可以嵌套
type Tree[K comparable, V any] struct {
    key      K
    value    V
    left     *Tree[K, V]
    right    *Tree[K, V]
}

func NewTree[K comparable, V any](k K, v V) *Tree[K, V] {
    return &Tree[K, V]{key: k, value: v}
}

// 使用
tree := NewTree("root", 0)
tree.left = NewTree("left", 1)
tree.right = NewTree("right", 2)

多类型参数的"约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 所有类型参数都可以有独立约束
func ComplexFunction[
    K comparable,      // K必须可比较
    V interface {      // V必须有特定方法
        String() string
    },
    T int | float64    // T是数值类型
](k K, v V, t T) {
    // ...
}

多类型参数的"注意事项"

1
2
3
4
5
6
7
// ⚠️ 类型参数不能有递归限制
// ❌ 错误:
// type Recursive[T interface{ *T }] struct {}

// ⚠️ 类型参数不能同时出现在约束和普通参数中
// ❌ 错误:
// func F[T int](x T, y T int) {}  // 重复

多类型参数的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ✅ 推荐:类型参数有实际意义
[K comparable, V any]  // K=Key, V=Value
[First any, Second any]

// ❌ 避免:类型参数过多(超过3个可能过于复杂)
[A any, B any, C any, D any, E any]

// ✅ 使用:标准库风格的命名
[K comparable, V any]
[T any]

小剧场

乐高积木:

小明:“我想用乐高拼一个机器人!”

爸爸:“好的,但你需要指定用什么材料(类型参数)。”

小明:“红色(string)塑料件和蓝色(int)塑料件!”

爸爸:“没问题![K, V]=[string, int]!”

小明:“那我要拼一个汽车!”

爸爸:“还是用[string, int]!”

小明:“可是汽车和机器人形状不一样啊!”

爸爸:“结构不一样,但材料类型组合是一样的。这就是多类型参数的威力——同样的材料类型,可以造出完全不同的东西!”

这就是多类型参数的作用——用有限的"材料类型",创造无限的可能!

下一个小节预告:33.1.2 类型实参——把"模板"变成"真实商品"!


33.1.2 类型实参

类型实参——当你调用泛型函数或创建泛型类型实例时,传入的具体类型就是"类型实参",相当于把"建筑模板"变成"实际建筑"的过程。

1
2
3
4
5
6
7
8
9
// 定义泛型函数:类型形参是T
func Identity[T any](x T) T {
    return x
}

// 调用时传入int作为类型实参
result := Identity[int](42)
//                 ↑
//                 类型实参:int

类型实参的"身份证解读"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 泛型函数调用
Identity[int](42)
//     ↑    ↑
//     │    └── 普通参数
//     └── 类型实参:int

// 泛型类型创建
var stack *Stack[string] = New[string]()
//                  ↑
//                  类型实参:string

类型实参的"两种指定方式"

1
2
3
4
5
6
7
// 方式1:显式指定
result := Identity[int](42)
stack := NewStack[string]()

// 方式2:类型推断(Go自动推断)
result := Identity(42)        // 推断为int
stack := NewStack[string]()  // 必须显式

类型实参的"类型推断"规则

graph TD
    A["类型推断规则"] --> B["可以从参数推断"]
    A --> C["可以从返回值推断"]
    A --> D["必要时必须显式指定"]
    
    B --> B1["Identity(42) → int"]
    C --> C1["func() T → result := get()"]
    D --> D1["泛型类型创建时"]
    
    style A fill:#FFD700

类型实参的"推断示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

// 泛型函数
func Max[T comparable](a, b T) T {
    if a > b {
        return a
    }
    return b
}

func main() {
    // 显式指定
    m1 := Max[int](1, 2)

    // 类型推断(Go从参数推断出T=int)
    m2 := Max(1, 2)

    // 类型推断(Go从参数推断出T=string)
    m3 := Max("a", "b")

    fmt.Println(m1, m2, m3)  // 2 2 b
}

类型实参的"泛型类型实例化"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 泛型结构体
type Stack[T any] struct {
    items []T
}

// 泛型方法
func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

// 实例化时必须指定类型实参
stack := &Stack[string]{}
stack.Push("hello")
stack.Push("world")

类型实参的"具体类型"要求

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 类型实参必须是具体类型
// 不能是接口类型(除非通过类型别名)

// ✅ 正确:具体类型
Stack[int]
Stack[string]
Stack[MyStruct]

// ❌ 错误:不能直接用接口
Stack[interface{}]  // 不推荐,应该用any

// ✅ 正确:用类型别名
type Stringer = interface{ String() string }
Stack[Stringer]  // 实际上很少这样用

类型实参的"嵌套泛型"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 嵌套使用类型实参
type TreeNode[T any] struct {
    value T
    left  *TreeNode[T]
    right *TreeNode[T]
}

// 使用具体类型实例化
node := &TreeNode[int]{
    value: 10,
    left: &TreeNode[int]{value: 5},
    right: &TreeNode[int]{value: 15},
}

// 嵌套泛型
type Pair[K, V any] struct {
    Key   K
    Value V
}

nested := Pair[string, *TreeNode[float64]]{
    Key:   "root",
    Value: &TreeNode[float64]{value: 3.14},
}

类型实参的"方法重载"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 同一个泛型类型,不同类型实参,是不同的类型
type Stack[T any] struct {
    items []T
}

s1 := &Stack[int]{}
s2 := &Stack[string]{}

// s1和s2是不同类型,不能互相赋值
// s1 = s2  // 编译错误

**类型实参的"编译器处理":

graph TD
    A["调用泛型函数"] --> B["传入类型实参"]
    B --> C["编译器生成具体代码"]
    C --> D["int版本函数体"]
    C --> E["string版本函数体"]
    C --> F["实际调用具体版本"]
    
    style A fill:#87CEEB
    style F fill:#90EE90

类型实参的"注意事项"

1
2
3
4
5
6
7
// ⚠️ 每个不同的类型实参组合都会生成一份代码
// ⚠️ 类型实参过多可能导致编译产物变大
// ⚠️ 类型实参必须满足类型约束

// 例如:约束是comparable,但你传入了不可比较的类型
func F[T comparable](a, b T) {}
// F[[]int]{}  // 编译错误![]int不可比较

类型实参的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ✅ 推荐:让Go推断类型
numbers := []int{1, 2, 3}
sum := Sum(numbers)  // Go推断T=int

// ✅ 必要时显式指定
var emptyStack = NewStack[string]()  // 必须指定

// ❌ 避免:过于复杂的类型实参
type Complex = map[string]struct{ x, y int }
result := Process[Complex](data)

类型实参的"与模板参数的区别"

概念类比
类型形参函数的形式参数
类型实参函数调用时的实际参数
[T any]参数列表
[int]具体传入的值

小剧场

烤箱和模具的故事:

厨师(开发者):“我要做一批饼干!”

助手:“好的,您用的是什么形状的模具(类型实参)?”

厨师:“星星模具(string)!”

助手开始用星星模具(string)做面团(代码):

1
MakeCookies[star]()

厨师:“等等,我还想做一批心形的!”

助手:“没问题,换心形模具(string)!”

1
MakeCookies[heart]()

厨师:“太好了!同样的烤箱(泛型函数),不同的模具(类型实参),做出不同的饼干!”

这就是类型实参的作用——把泛型的"模板"变成具体的"产品"!

下一个小节预告:33.1.3 类型参数约束——给你的类型参数画个圈!


33.1.3 类型参数约束

类型参数约束——这是给类型参数画的一个"圈",规定了哪些类型可以进入这个泛型函数或泛型类型。就像游乐场对游客的身高要求——不是所有人都能坐过山车,必须达到一定"高度"才行!

1
2
3
4
5
6
7
// [T any] - T可以是任意类型,就像任何人都能进公园
func Print[T any](v T) { }

// [T int] - T只能是int,就像只有1.4米以上的人才能坐过山车
func Double[T int](v T) T {
    return v * 2
}

约束的"两种写法"

1
2
3
4
5
6
7
8
9
// 写法1:inline约束(直接在方括号里写)
func F[T int | float64](x T) T { }

// 写法2:接口约束(用interface定义)
type Number interface {
    int | int32 | int64 | float32 | float64
}

func F[T Number](x T) T { }

any约束——最宽松的约束

1
2
3
4
5
6
7
8
9
// any = interface{},表示任意类型
func Identity[T any](x T) T {
    return x
}

// 任何类型都可以
Identity(42)      // int
Identity("hello") // string
Identity([]int{1, 2}) // []int

comparable约束——必须能比较

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// comparable表示可以使用 == 和 != 进行比较
func Equal[T comparable](a, b T) bool {
    return a == b
}

// 可以比较的类型
Equal(1, 2)        // int
Equal("a", "b")    // string

// 不可以比较的类型会报错
// Equal([]int{1}, []int{2})  // 编译错误!slice不可比较

内置约束类型

约束含义示例
any任意类型T any
comparable可用==和!=比较T comparable
int 系列整数类型T int | int32
float 系列浮点数类型T float32 | float64
uint 系列无符号整数T uint | uint64

自定义约束

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 用interface定义约束
type SignedNumber interface {
    int | int32 | int64 | float32 | float64
}

func Abs[T SignedNumber](v T) T {
    if v < 0 {
        return -v
    }
    return v
}

Abs(-10)   // works: int
Abs(-3.14) // works: float64
// Abs("hello") // 编译错误:string不在约束中

约束的"或"关系

1
2
3
4
5
6
// 用 | 表示"或"关系
[T int | string]      // T可以是int或string
[T int | float64]    // T可以是int或float64

// 可以组合多个
[T int8 | int16 | int32 | int64]

约束的"与方法约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 约束可以要求类型具有某些方法
type Stringer interface {
    String() string
}

func ToString[T Stringer](v T) string {
    return v.String()
}

// 任何实现了String()方法的类型都满足约束
type Person struct {
    Name string
}

func (p Person) String() string {
    return p.Name
}

p := Person{Name: "Alice"}
fmt.Println(ToString(p))  // Alice

约束的"叠加"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 可以同时有基础类型约束和方法约束
type Addable interface {
    int | int32 | int64 | float32 | float64
}

type Stringable interface {
    String() string
}

func Process[T Addable & Stringable](v T) {
    // T必须同时满足Addable和Stringable
    fmt.Println(v.String())
}

~tilde约束——基于类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// ~T 表示底层类型是T的所有类型
type MyInt int

func Double[T ~int](v T) T {
    return v * 2
}

// MyInt的底层是int,所以可以用
Double(MyInt(5))  // works! 推断为MyInt

// 但不能用于string
// Double("hello")  // 编译错误

约束的"结构"详解

1
2
3
4
5
6
7
// 约束interface可以包含:
type MyConstraint interface {
    int | string              // 并集约束
    ~int                     // 近似约束
    comparable               // 基础约束
    ~float64 & comparable   // 交集约束(实际很少用)
}

常用约束的"简写形式"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 标准库提供的约束
// any = interface{}

// comparable 已内置
// ~int 已支持

// 标准库的constraints包提供常用约束
import "golang.org/x/exp/constraints"

// Ordered: 可排序的类型
func Sort[T constraints.Ordered](values []T) { }

// Integer: 整数类型
// Float: 浮点类型
// Complex: 复数类型

约束的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// ✅ 约束尽量精确
func Sum[T int | float64](values []T) T { }

// ✅ 如果方法约束太复杂,使用自定义类型
type MyConstraint interface {
    int | float64
    comparable
    ~int
}

// ❌ 避免过于宽松的约束
func F[T any](x T) {  // 仅在真正需要any时使用

**约束的"编译时检查":

1
2
3
4
5
6
7
// 编译时检查类型是否满足约束
func Double[T int | float64](v T) T {
    return v * 2
}

// 编译错误:string不满足约束
// Double("hello")  // 编译错误!

约束的"注意事项"

1
2
3
4
5
6
7
8
// ⚠️ 约束在编译时检查
// ⚠️ 约束不运行时检查
// ⚠️ 方法约束必须正确签名

// 错误示例
type BadConstraint interface {
    DoSomething(int)  // 编译错误!需要返回值
}

小剧场

游乐场的故事:

游乐场规则(约束):“想坐过山车?身高必须1.4米以上!”

小孩(类型):“我9岁了,想坐过山车!”

工作人员(编译器):“让我量一下(编译时检查)。嗯,你身高1.3米,不满足约束(约束int | float64),不能坐!”

小孩:“那我能不能坐旋转木马?”

工作人员:“旋转木马没有身高限制(约束any),谁都能坐!”

这就是类型参数约束的作用——规定哪些类型可以使用这个泛型!

下一个小节预告:33.2 类型约束——深入了解约束的本质!


33.2 类型约束

类型约束是泛型的"灵魂",它决定了泛型代码能接受什么类型的"输入",能产生什么类型的"输出"。掌握类型约束,就等于掌握了泛型的精髓。

33.2.1 约束接口

约束接口——这是一种特殊的接口,它不仅能描述方法要求,还能描述类型要求。它就像是一个"复合型招聘要求",既要求你会某些技能(方法),又要求你具备某些资质(基础类型)。

1
2
3
4
5
6
7
8
9
// 普通接口:只约束方法
type Reader interface {
    Read(p []byte) (n int, err error)
}

// 约束接口:既约束方法,又约束基础类型
type Number interface {
    int | int32 | int64 | float32 | float64
}

约束接口的"双重身份"

graph LR
    A["约束接口"] --> B["作为接口使用"]
    A --> C["作为约束使用"]
    
    B --> B1["定义方法集合"]
    B1 --> B2["定义类型集合"]
    
    C --> C1["泛型类型约束"]
    
    style A fill:#FFD700

约束接口的定义

1
2
3
4
5
6
7
8
9
// 定义一个约束接口
type SignedInteger interface {
    int | int32 | int64
}

// 在泛型中使用
func Double[T SignedInteger](v T) T {
    return v * 2
}

约束接口的"扩展"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 约束可以继承
type IntOrFloat interface {
    int | int32 | int64 | float32 | float64
}

type Numeric interface {
    IntOrFloat  // 继承IntOrFloat
}

// 或者更复杂
type AdvancedNumeric interface {
    IntOrFloat
    int8 | int16  // 添加更多
}

约束接口的方法约束

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 既要求基础类型,又要求方法
type StringableNumber interface {
    int | int32 | int64 | float32 | float64
    String() string
}

func PrintNum[T StringableNumber](n T) {
    fmt.Println(n.String())
}

// 测试
type MyNum int

func (m MyNum) String() string {
    return strconv.Itoa(int(m))
}

PrintNum(MyNum(42))  // works!

约束接口的"空约束"

1
2
3
4
5
6
7
// 空约束 = 任意类型
type anyConstraint interface {
}

func F[T anyConstraint](x T) { }
// 等价于
func F[T any](x T) { }

约束接口的"类型并集"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 用 | 连接多个类型
type IntOrString interface {
    int | string
}

func Process[T IntOrString](v T) {
    fmt.Println(v)
}

Process(42)       // works
Process("hello")   // works
// Process(3.14)   // 编译错误!float64不在约束中

约束接口的"命名惯例"

1
2
3
4
5
6
7
8
// Go社区的命名惯例:
type (
    SignedInteger interface{ int | int32 | int64 | int8 | int16 }
    UnsignedInteger interface{ uint | uint8 | uint16 | uint32 | uint64 }
    Float interface{ float32 | float64 }
    Integer interface{ SignedInteger | UnsignedInteger }
    Ordered interface{ Integer | Float | ~string }
)

约束接口与普通接口的"区别"

特性普通接口约束接口
用途定义行为约束类型
方法必须有可选
类型不能有可以有
用于泛型间接(通过类型约束)直接

约束接口的"简化写法"

1
2
3
4
5
6
7
// 不需要用interface{}关键字
type Constraint[T any] interface {
    ~T
}

// 或者直接写
func F[T interface{ int | string }](x T) { }

约束接口的"方法集"

1
2
3
4
5
6
7
8
9
// 约束可以包含方法
type Serialize[T any] interface {
    Serialize() []byte
}

func Save[T Serialize[T]](v T) error {
    data := v.Serialize()
    return os.WriteFile("data.bin", data, 0644)
}

约束接口的"实际应用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
    "fmt"
)

// 定义数值类型的约束
type Number interface {
    int | int8 | int16 | int32 | int64 | float32 | float64
}

// 泛型求和函数
func Sum[T Number](values ...T) T {
    var result T
    for _, v := range values {
        result += v
    }
    return result
}

// 泛型最大最小值
func Min[T Number](values ...T) T {
    if len(values) == 0 {
        return *new(T)
    }
    min := values[0]
    for _, v := range values[1:] {
        if v < min {
            min = v
        }
    }
    return min
}

func main() {
    fmt.Println(Sum(1, 2, 3, 4, 5))              // 15
    fmt.Println(Sum(1.1, 2.2, 3.3))              // 6.6
    fmt.Println(Min(5, 3, 8, 1, 9))              // 1
}

约束接口的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ✅ 约束定义要清晰
type Numeric interface {
    int | int32 | int64 | float32 | float64
}

// ✅ 约束要有实际意义
type ComparableOrdered interface {
    comparable
}

// ✅ 复杂约束单独定义
type MyComplexConstraint interface {
    int | string
    ~int
    comparable
}

小剧场

招聘会的故事:

招聘方(开发者):“我们需要招聘一个能干活的人(泛型函数)!”

HR(约束接口):“好的,我们的招聘要求是:必须会Java或Python(类型约束),同时要有3年经验(方法约束)!”

候选人张三(int类型):“我有10年Java经验!”

HR(编译器):“满足要求!录用!”

候选人李四(float64类型):“我有5年Python经验!”

HR:“满足要求!录用!”

候选人王五(bool类型):“我也会Java,但是我没有3年经验(bool不在类型约束中)!”

HR:“不好意思,您的类型不符合我们的要求!”

这就是约束接口的作用——既规定了技能(类型),又规定了经验(方法)!

下一个小节预告:33.2.2 预定义约束——Go标准库给你的"神器"!


33.2.2 预定义约束

Go标准库为泛型提供了几个"开箱即用"的预定义约束,让你的泛型代码更加简洁高效!

33.2.2.1 any

any——这是Go泛型中最"万能"的约束,意味着"可以是任意类型"。就像一张空白支票,你想填什么数字都行!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// any = interface{}
// any是最宽松的约束

func Identity[T any](x T) T {
    return x
}

// 任何类型都可以
Identity(42)           // int
Identity("hello")      // string
Identity([]byte{1,2})  // []byte
Identity(map[string]int{"a": 1})  // map

any的本质

1
2
3
4
5
6
// any实际上是空接口的别名
type any = interface{}

// 所以下面的写法等价:
func F1[T any](x T) { }
func F2[T interface{}](x T) { }

any的使用场景

graph LR
    A["any的使用场景"] --> B["容器类型"]
    A --> C["通用工具函数"]
    A --> D["需要存储任意类型的场景"]
    
    B --> B1["Stack[T any]"]
    C --> C1["Print、Copy等"]
    D --> D1["缓存、通用存储"]

any的"万能容器"示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 泛型栈,可以存储任意类型
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
}

func (s *Stack[T]) Pop() (T, bool) {
    if len(s.items) == 0 {
        return *new(T), false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

func main() {
    intStack := &Stack[int]{}
    intStack.Push(1)
    intStack.Push(2)
    fmt.Println(intStack.Pop())  // 2 true

    strStack := &Stack[string]{}
    strStack.Push("hello")
    strStack.Push("world")
    fmt.Println(strStack.Pop())  // world true
}

any的"类型安全"问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 虽然any接受任意类型,但Go仍会在编译时检查类型
func PrintAll[T any](items []T) {
    for _, item := range items {
        fmt.Println(item)
    }
}

// 正确用法
PrintAll([]int{1, 2, 3})
PrintAll([]string{"a", "b"})

// 错误用法(类型不匹配)
// PrintAll("hello")  // 编译错误!字符串不是切片

any的"注意事项"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// ⚠️ any不提供任何类型保证
// ⚠️ 传入any后,失去了具体类型的特殊能力
// ⚠️ 例如:不能对any类型的值做算术运算

func Double[T any](v T) T {
    return v * 2  // 编译错误!T是any,不知道如何相乘
}

// ✅ 应该用约束
func Double[T int | float64](v T) T {
    return v * 2  // 编译通过
}

any的"最佳使用时机"

1
2
3
4
5
6
7
8
// ✅ 适合使用any的场景:
// 1. 容器类型,不关心元素的操作
// 2. 只需要打印或复制的函数
// 3. 需要存储和返回任意类型的场景

// ❌ 不适合使用any的场景:
// 1. 需要对值进行类型特定的操作
// 2. 需要保证值的某些能力

any与空接口的历史

1
2
3
4
5
6
7
// Go 1.18之前的写法
var v interface{} = 42

// Go 1.18+的写法
var v any = 42

// 两者等价,但any更简洁

小剧场

哆啦A梦的口袋:

大雄:“哆啦A梦,我要一个任意门!”

哆啦A梦:“没问题!(从口袋里掏出)”

大雄:“我还要竹蜻蜓!”

哆啦A梦:“也有!(又掏出一个)”

大雄:“哇,你这个口袋是any类型的吗?什么都能装!”

哆啦A梦:“没错!这就是any——最万能的约束,什么类型都能接受!”

这就是any约束的作用——一个"万能口袋",什么都能装!


33.2.2.2 comparable

comparable——这是一个"较真"的约束,它要求类型必须能够用==!=进行比较。就像是"只有能分出高下的选手才能参加比赛"!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// comparable约束要求类型可以用 == 和 != 比较
func Equal[T comparable](a, b T) bool {
    return a == b
}

// 可以比较的类型
Equal(1, 2)           // int: false
Equal("a", "a")      // string: true
Equal(1.0, 1.0)      // float64: true

// 不能比较的类型会编译错误
// Equal([]int{1}, []int{2})  // 编译错误!切片不能比较

comparable的本质

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// comparable是一个预定义的约束
// 表示可以使用 == 和 != 进行比较的类型

// 内置的可比较类型:
// - 整型(int, uint, rune, byte, int8, uint8等)
// - 浮点数(float32, float64)- 注意NaN不相等
// - 字符串(string)
// - 布尔(bool)
// - 指针
// - 通道
// - 接口(当具体类型可比较时)
// - 结构体(当所有字段可比较时)
// - 数组(当元素类型可比较时)

// 不可比较的类型:
// - 切片([]int)
// - 映射(map[K]V)
// - 函数(func)

comparable的使用场景

graph LR
    A["comparable使用场景"] --> B["去重"]
    A --> C["查找"]
    A --> D["作为map的键"]
    A --> E["集合操作"]
    
    style A fill:#FFD700

comparable的"去重函数"示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 泛型去重函数
func Unique[T comparable](items []T) []T {
    seen := make(map[T]bool)
    result := []T{}
    
    for _, item := range items {
        if !seen[item] {
            seen[item] = true
            result = append(result, item)
        }
    }
    return result
}

func main() {
    nums := []int{1, 2, 2, 3, 3, 3, 4}
    fmt.Println(Unique(nums))  // [1 2 3 4]

    strs := []string{"a", "b", "a", "c", "b"}
    fmt.Println(Unique(strs))  // [a b c]
}

comparable的"查找函数"示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 泛型查找函数
func Contains[T comparable](items []T, target T) bool {
    for _, item := range items {
        if item == target {
            return true
        }
    }
    return false
}

func IndexOf[T comparable](items []T, target T) int {
    for i, item := range items {
        if item == target {
            return i
        }
    }
    return -1
}

func main() {
    nums := []int{10, 20, 30, 40, 50}
    
    fmt.Println(Contains(nums, 30))    // true
    fmt.Println(Contains(nums, 35))    // false
    fmt.Println(IndexOf(nums, 30))      // 2
    fmt.Println(IndexOf(nums, 35))     // -1
}

comparable的"Set实现"示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 泛型Set
type Set[T comparable] struct {
    items map[T]struct{}
}

func NewSet[T comparable]() *Set[T] {
    return &Set[T]{items: make(map[T]struct{})}
}

func (s *Set[T]) Add(items ...T) {
    for _, item := range items {
        s.items[item] = struct{}{}
    }
}

func (s *Set[T]) Has(item T) bool {
    _, ok := s.items[item]
    return ok
}

func (s *Set[T]) Remove(item T) {
    delete(s.items, item)
}

func (s *Set[T]) Size() int {
    return len(s.items)
}

func (s *Set[T]) Union(other *Set[T]) *Set[T] {
    result := NewSet[T]()
    for item := range s.items {
        result.Add(item)
    }
    for item := range other.items {
        result.Add(item)
    }
    return result
}

func main() {
    s1 := NewSet[int]()
    s1.Add(1, 2, 3, 4)
    
    s2 := NewSet[int]()
    s2.Add(3, 4, 5, 6)
    
    union := s1.Union(s2)
    fmt.Println(union.Has(1))  // true
    fmt.Println(union.Has(5))  // true
    fmt.Println(union.Size())  // 6
}

comparable与map的键约束

1
2
3
4
5
6
7
8
// map的键必须是可比较的
// comparable约束确保了这一点

func GroupBy[T comparable, V any](items []T, keyFn func(V) T) map[T][]V {
    groups := make(map[T][]V)
    // ...
    return groups
}

comparable的"注意事项"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ⚠️ 浮点数的NaN值得注意
import "math"

// NaN != NaN 是true!
// 这是浮点数的特性,comparable仍认为float64可比较
func F[T comparable](a, b T) bool {
    return a == b
}

f1 := math.NaN()
f2 := math.NaN()
fmt.Println(F(f1, f2))  // false(符合IEEE754标准)

// ⚠️ 切片和map不可比较
// var m map[T]V  // T不能是[]int
// var s []T      // T不能是map[K]V

comparable的最佳实践

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ✅ 使用comparable确保类型可以比较
func Unique[T comparable](items []T) []T

// ✅ comparable用于map键类型
type Cache[T comparable, V any] struct {
    data map[T]V
}

// ❌ 不要对浮点数期望严格的相等
// 浮点数比较最好用误差范围
func roughlyEqual(a, b float64) bool {
    const epsilon = 1e-9
    return math.Abs(a-b) < epsilon
}

comparable与结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 结构体也可以是comparable
// 条件:所有字段都是可比较的

type Point struct {
    X, Y int
}

type Person struct {
    Name string
    Age  int
}

func main() {
    p1 := Point{X: 1, Y: 2}
    p2 := Point{X: 1, Y: 2}
    
    fmt.Println(p1 == p2)  // true!结构体可比较
    
    // 但如果结构体包含切片,就不可比较
    // type Bad struct {
    //     X int
    //     Data []int  // 编译错误!切片不可比较
    // }
}

小剧场

运动会的故事:

裁判(comparable约束):“参加跑步比赛的选手,必须能分出快慢(可以用==比较)!”

兔子:“我跑得快!”

裁判:“好,你通过审查!”

乌龟:“我跑得慢…但我能分出谁更快!”

裁判:“好,你也可以参加!”

蜗牛:“我背着一堆东西(切片),不知道该怎么比快慢…”

裁判:“抱歉,背着这些东西(不可比较类型)的选手不能参加!”

comparable就是这样工作的——只有能"分出高下"的类型才能使用相关的泛型函数!

下一个小节预告:33.2.3 类型集——从另一个角度理解约束!


33.2.3 类型集

类型集——约束本质上定义了一个"类型集合",只有属于这个集合的类型才能作为泛型的实参。联合类型(|)、近似元素(~)和类型枚举都是操作这个集合的工具。

33.2.3.1 联合类型 |

联合类型——用|连接的多个类型,表示"这些类型中的任意一个都行"。就像"招聘要求:会Java或会Python或会Go"。

1
2
3
4
5
6
7
8
// int | string 表示 int 或 string 类型
func Process[T int | string](v T) {
    fmt.Println(v)
}

Process(42)      // works
Process("hello") // works
// Process(3.14) // 编译错误!float64不在集合中

联合类型的"或"逻辑

graph LR
    A["类型A"] --> C["A \| B"]
    B["类型B"] --> C
    
    C --> D["A或B都接受"]
    D --> E["A | B = {A, B}"]
    
    style C fill:#FFD700

联合类型的"多选一"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 数值类型的联合
type Number interface {
    int | int8 | int16 | int32 | int64 | float32 | float64
}

// 使用
func Double[T Number](v T) T {
    return v * 2
}

// 字符类型的联合
type CharLike interface {
    string | []byte | rune
}

func Len[T CharLike](v T) int {
    return len(v)
}

Len("hello")        // 5
Len([]byte{'a','b'}) // 2
Len([]rune{'你','好'}) // 2

联合类型的"链式写法"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 多个类型联合
type IntOrFloatOrString interface {
    int | int32 | int64 | float32 | float64 | string
}

// 或者分行写(更清晰)
type Numeric interface {
    int | int32 | int64 | float32 | float64
}

type MoreTypes interface {
    Numeric | string | bool
}

联合类型的"方法约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 联合类型 + 方法约束
type Printable interface {
    int | string | float64
    ~string
    String() string  // 要求有String()方法
}

func Print[T Printable](v T) {
    fmt.Println(v.String())
}

type MyInt int

func (m MyInt) String() string {
    return strconv.Itoa(int(m))
}

Print(MyInt(42))  // works!

联合类型的"嵌套"

1
2
3
4
// 联合类型可以嵌套
type A interface { int | string }
type B interface { float64 | bool }
type C interface { A | B }  // int | string | float64 | bool

联合类型的"简化"

1
2
3
4
5
6
7
// 复杂的联合可以简化
type AllInts interface {
    int | int8 | int16 | int32 | int64 |
    uint | uint8 | uint16 | uint32 | uint64
}

// 但Go编译器会自动处理一些简化

联合类型的"实际示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import "fmt"

// 定义可比较的数值类型
type OrderedNumber interface {
    int | int8 | int16 | int32 | int64 | float32 | float64
}

func Max[T OrderedNumber](values ...T) T {
    if len(values) == 0 {
        return *new(T)
    }
    max := values[0]
    for _, v := range values[1:] {
        if v > max {
            max = v
        }
    }
    return max
}

func Min[T OrderedNumber](values ...T) T {
    if len(values) == 0 {
        return *new(T)
    }
    min := values[0]
    for _, v := range values[1:] {
        if v < min {
            min = v
        }
    }
    return min
}

func main() {
    fmt.Println(Max(1, 5, 3, 9, 2))        // 9
    fmt.Println(Max(1.1, 5.5, 3.3))       // 5.5
    fmt.Println(Min(1, 5, 3, 9, 2))        // 1
}

联合类型的"注意事项"

1
2
3
4
5
6
7
8
// ⚠️ 联合类型是"或"关系,不是"且"关系
// T int | string 表示:T可以是int或者string

// ⚠️ 联合类型不能有重复
// type Bad interface { int | int }  // 重复,编译错误

// ⚠️ 联合类型的顺序不影响
// int | string 等价于 string | int

小剧场

点餐系统的故事:

服务员(联合类型):“您可以点咖啡、茶或者可乐(int | string | float64)!”

顾客A:“我要咖啡!”

服务员:“好的!”

顾客B:“我要茶!”

服务员:“好的!”

顾客C:“我要可乐!”

服务员:“好的!”

顾客D:“我要橙汁!”

服务员:“不好意思,橙汁不在我们的菜单上(float64不在集合中)!”

联合类型就是这样工作的——明确列出所有"可以接受"的类型!


33.2.3.2 近似元素 ~

近似元素——用~开头的类型表示"以这个类型为基础的所有类型"。就像是"所有以int为底层的自定义类型"都能接受,不管你叫它MyInt还是CustomInt,只要底层是int就行!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ~int 表示所有以int为底层类型的类型
type MyInt int
type CustomInt int
type AnotherInt int

func Double[T ~int](v T) T {
    return v * 2
}

// MyInt、CustomInt、AnotherInt都可以用
Double(MyInt(5))      // works!
Double(CustomInt(10)) // works!
Double(AnotherInt(15)) // works!
Double(42)            // works! int本身也可以

~的作用原理

graph LR
    A["int"] --> B["MyInt (底层int)"]
    A --> C["CustomInt (底层int)"]
    A --> D["int"]
    
    E["~int"] --> F["接受所有底层为int的类型"]
    
    style A fill:#87CEEB
    style F fill:#90EE90

~的"包容性"示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 没有~时
type Int int

func DoubleWithoutTilde[T int](v T) T {
    return v * 2
}

// Int(5) 不能用,因为T只接受int
// DoubleWithoutTilde(Int(5))  // 编译错误!

// 使用~后
func DoubleWithTilde[T ~int](v T) T {
    return v * 2
}

// Int(5) 可以用
DoubleWithTilde(Int(5))  // works!

~的"自定义类型保护"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 场景:你想让泛型函数接受所有"int类型",包括自定义int类型

type UserID int
type OrderID int

// 使用~int,所有底层是int的类型都可以
func HashID[T ~int](id T) int {
    return int(id) * 2654435761 % 1000000
}

uid := UserID(12345)
oid := OrderID(67890)

fmt.Println(HashID(uid))  // works!
fmt.Println(HashID(oid))  // works!
fmt.Println(HashID(12345)) // works!

~与类型约束的结合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// ~与|结合使用
type SignedInt interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

func DoubleSigned[T SignedInt](v T) T {
    return v * 2
}

type MyInt int
type MyFloat float64

DoubleSigned(MyInt(5))   // works!
DoubleSigned(int64(10))  // works!
// DoubleSigned(MyFloat(3.14)) // 编译错误!float64不在~int系列中

~的"不能滥用"原则

1
2
3
4
5
6
7
8
// ⚠️ ~只能用在类型前面,不能单独使用
// type Bad ~int  // 编译错误!

// ⚠️ ~后面必须是基础类型,不能是接口
// type Bad ~interface{}  // 编译错误!

// ⚠️ ~不能用在非类型标识符上
// func Bad[T ~~int]() {}  // 编译错误!

~的"标准库示例"

1
2
3
4
5
6
7
// Go标准库的constraints包使用了~
type Ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
    ~float32 | ~float64 |
    ~string
}

~的"实战应用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
    "fmt"
)

// 定义一个"可哈希"的约束
// 所有以int或string为底层的类型都满足
type Hashable interface {
    ~int | ~string
}

// 泛型哈希函数
func Hash[T Hashable](v T) int {
    // 简单的哈希实现
    switch any(v).(type) {
    case int:
        return int(v.(type int)) * 31
    case string:
        h := 0
        for _, c := range v.(type string) {
            h = h*31 + int(c)
        }
        return h
    }
    return 0
}

// 自定义ID类型
type UserID int
type OrderID int
type UserName string

func main() {
    uid := UserID(100)
    oid := OrderID(200)
    name := UserName("Alice")

    fmt.Println(Hash(uid))   // works!
    fmt.Println(Hash(oid))   // works!
    fmt.Println(Hash(name))  // works!
    fmt.Println(Hash(123))   // works!
    fmt.Println(Hash("Bob")) // works!
}

~的"注意事项"

1
2
3
4
5
6
7
// ⚠️ ~T 包括 T 本身
// ~int 包括 int、MyInt、CustomInt等所有int底层类型

// ⚠️ ~只能用于用户自定义类型的基础类型
// 内置类型不能使用~
type Bad1 ~int    // 编译错误!~不能用于内置类型
type Bad2 ~string // 编译错误!

小剧场

家族遗传的故事:

家族族谱(类型系统):

老祖宗int有三个后代:

  • MyInt(长孙)
  • CustomInt(二孙)
  • OtherInt(幺孙)

招聘要求(~int):“只要是我们int家族的后代,都可以来工作!”

MyInt:“我是int家族的长孙,我来应聘!”

招聘官(编译器):“欢迎!”

CustomInt:“我是int家族的二孙,我也来!”

招聘官:“欢迎!”

其他家族的人(float64):“我也能来吗?”

招聘官:“不好意思,我们只招int家族的后代(~int),您是float家族的人,不在招聘范围内!”

这就是~的作用——不仅接受基础类型本身,还接受它的所有"后代"!


33.2.3.3 类型枚举

类型枚举——通过约束接口明确定义"这个泛型只接受这些具体的类型",就像是一个"只接受特定名单上的人"的邀请函。

1
2
3
4
5
6
7
8
9
// 明确列出所有可接受的类型
type Shape interface {
    Circle | Rectangle | Triangle
}

// 使用
func Area[T Shape](s T) float64 {
    return s.Area()  // 假设所有Shape都有Area()方法
}

类型枚举的"精确控制"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ❌ 不使用枚举:太宽泛
func Process[T any](v T) { }

// ✅ 使用枚举:精确控制
type ValidInput interface {
    int | string | bool | float64
}

func Process[T ValidInput](v T) {
    fmt.Println(v)
}

类型枚举的"受限集合"

graph LR
    A["类型枚举"] --> B["明确的类型列表"]
    A --> C["编译时检查"]
    A --> D["类型安全保证"]
    
    style A fill:#FFD700

类型枚举的"实际应用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义有限的类型集合
type HTTPMethod interface {
    GET | POST | PUT | DELETE | PATCH
}

func CallAPI[T HTTPMethod](url string) {
    switch any(*new(T)).(type) {
    case GET:
        fmt.Println("GET", url)
    case POST:
        fmt.Println("POST", url)
    case PUT:
        fmt.Println("PUT", url)
    case DELETE:
        fmt.Println("DELETE", url)
    case PATCH:
        fmt.Println("PATCH", url)
    }
}

// 类型别名
type GET struct{}
type POST struct{}
type PUT struct{}
type DELETE struct{}
type PATCH struct{}

func main() {
    CallAPI[GET]("/users")
    CallAPI[POST]("/users")
}

类型枚举与方法的结合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 类型枚举 + 方法约束
type Operation interface {
    Add | Subtract | Multiply | Divide
    Compute(a, b float64) float64
}

func Calculate[T Operation](a, b float64) float64 {
    var t T
    return t.Compute(a, b)
}

// 定义运算类型
type Add struct{}
func (Add) Compute(a, b float64) float64 { return a + b }

type Subtract struct{}
func (Subtract) Compute(a, b float64) float64 { return a - b }

type Multiply struct{}
func (Multiply) Compute(a, b float64) float64 { return a * b }

type Divide struct{}
func (Divide) Compute(a, b float64) float64 { return a / b }

func main() {
    fmt.Println(Calculate[Add](10, 5))      // 15
    fmt.Println(Calculate[Subtract](10, 5)) // 5
    fmt.Println(Calculate[Multiply](10, 5)) // 50
    fmt.Println(Calculate[Divide](10, 5))   // 2
}

类型枚举的"状态机"应用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 定义状态枚举
type State interface {
    Idle | Running | Paused | Stopped
}

type StateHandler[S State] struct {
    current S
}

func (h *StateHandler[S]) Transition(to S) {
    h.current = to
}

func (h *StateHandler[S]) GetState() S {
    return h.current
}

// 定义状态
type Idle struct{}
type Running struct{}
type Paused struct{}
type Stopped struct{}

func main() {
    handler := &StateHandler[Idle]{}
    fmt.Printf("Current state: %T\n", handler.GetState()) // Idle

    handler.Transition(Running{})
    fmt.Printf("Current state: %T\n", handler.GetState()) // Running
}

类型枚举的"注意事项"

1
2
3
4
5
// ⚠️ 类型枚举是"穷举式"的
// ⚠️ 适合类型数量有限的场景

// ⚠️ 不适合类型数量无限或未知的场景
// ⚠️ 这时应该用更通用的约束

类型枚举的最佳实践

1
2
3
4
5
6
7
8
// ✅ 类型枚举适合的场景:
// 1. 有限数量的类型
// 2. 需要穷举所有可能的类型
// 3. 编译时需要类型安全

// ❌ 不适合的场景:
// 1. 无限数量的类型
// 2. 需要动态添加新类型

类型枚举与联合类型的区别

特性类型枚举联合类型
定义方式命名类型匿名类型
使用场景有限类型无限类型
类型安全
可扩展性

小剧场

餐厅点餐的故事:

餐厅(泛型函数):“我们今天只提供四种套餐(类型枚举):”

套餐列表(State接口):

  • 套餐A(GET)
  • 套餐B(POST)
  • 套餐C(PUT)
  • 套餐D(DELETE)

顾客:“我要套餐A!”

餐厅:“好的,马上为您准备!”

顾客:“我要套餐E!”

餐厅:“不好意思,我们没有套餐E!只有A、B、C、D四种套餐可选!”

类型枚举就是这样工作的——明确告诉你"只能选这些",不在列表上的一律拒绝!

下一个小节预告:33.3 泛型类型——让数据结构更加灵活!


33.3 泛型类型

泛型类型是Go 1.18引入的一种强大特性,它让你可以定义一种"模板类型",然后用具体的类型来实例化出"真实类型"。就像建筑图纸和实际建筑的关系——图纸是泛型类型,盖出来的房子是具体类型。

33.3.1 泛型结构体

泛型结构体——一种带有类型参数的结构体模板,让同一结构体定义可以服务于多种数据类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 定义泛型结构体
type Box[T any] struct {
    content T
}

// 用具体类型实例化
intBox := Box[int]{content: 42}
strBox := Box[string]{content: "hello"}

// 访问内容
fmt.Println(intBox.content) // 42
fmt.Println(strBox.content) // hello

泛型结构体的"模板定义"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 单类型参数的泛型结构体
type Stack[T any] struct {
    items []T
    top  int
}

func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
    s.top++
}

func (s *Stack[T]) Pop() (T, bool) {
    if s.top == 0 {
        return *new(T), false
    }
    s.top--
    item := s.items[s.top]
    s.items = s.items[:s.top]
    return item, true
}

泛型结构体的"实例化"

graph LR
    A["泛型结构体定义"] --> B["Box[T any]"]
    B --> C["实例化1: Box[int]"]
    B --> D["实例化2: Box[string]"]
    B --> E["实例化3: Box[float64]"]
    
    style A fill:#87CEEB
    style C fill:#90EE90
    style D fill:#90EE90
    style E fill:#90EE90

泛型结构体的"多类型参数"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 两个类型参数的泛型结构体
type Pair[K, V any] struct {
    Key   K
    Value V
}

// 实例化
p1 := Pair[string, int]{Key: "age", Value: 25}
p2 := Pair[int, string]{Key: 1, Value: "one"}

// 嵌套使用
type TreeNode[T any] struct {
    value T
    left  *TreeNode[T]
    right *TreeNode[T]
}

泛型结构体的"方法定义"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 泛型结构体
type Container[T any] struct {
    elements []T
}

// 泛型方法:类型参数在结构体上
func (c *Container[T]) Add(e T) {
    c.elements = append(c.elements, e)
}

func (c *Container[T]) Get(index int) T {
    return c.elements[index]
}

func (c *Container[T]) Size() int {
    return len(c.elements)
}

// 泛型方法:引入新的类型参数
func (c *Container[T]) Map[U any](f func(T) U) []U {
    result := make([]U, len(c.elements))
    for i, e := range c.elements {
        result[i] = f(e)
    }
    return result
}

泛型结构体的"完整示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package main

import "fmt"

// 泛型链表节点
type ListNode[T any] struct {
    value T
    next  *ListNode[T]
}

// 泛型链表
type LinkedList[T any] struct {
    head *ListNode[T]
    size int
}

// 创建新链表
func NewLinkedList[T any]() *LinkedList[T] {
    return &LinkedList[T]{}
}

// 添加元素
func (l *LinkedList[T]) Append(value T) {
    node := &ListNode[T]{value: value}
    
    if l.head == nil {
        l.head = node
    } else {
        current := l.head
        for current.next != nil {
            current = current.next
        }
        current.next = node
    }
    l.size++
}

// 获取元素
func (l *LinkedList[T]) Get(index int) (T, bool) {
    if index < 0 || index >= l.size {
        return *new(T), false
    }
    
    current := l.head
    for i := 0; i < index; i++ {
        current = current.next
    }
    return current.value, true
}

// 链表长度
func (l *LinkedList[T]) Size() int {
    return l.size
}

func main() {
    list := NewLinkedList[int]()
    list.Append(1)
    list.Append(2)
    list.Append(3)
    
    fmt.Println("Size:", list.Size()) // 3
    
    for i := 0; i < list.Size(); i++ {
        if v, ok := list.Get(i); ok {
            fmt.Printf("Element %d: %d\n", i, v)
        }
    }
}

泛型结构体的"嵌套泛型"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 泛型结构体可以嵌套
type Tree[T any] struct {
    value T
    left  *Tree[T]
    right *Tree[T]
}

// 泛型结构体作为字段类型
type Result[T any] struct {
    Data  T
    Error error
}

type Response[T any] struct {
    Results []Result[T]
    Total   int
}

泛型结构体的"构造函数"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 泛型构造函数
type Config[T any] struct {
    value   T
    loaded  bool
}

// 构造函数
func NewConfig[T any](v T) *Config[T] {
    return &Config[T]{
        value:  v,
        loaded: true,
    }
}

// 带约束的泛型结构体
type ComparableBox[T comparable] struct {
    content T
}

func main() {
    // 使用构造函数
    cfg := NewConfig[string]("initial")
    fmt.Println(cfg.value) // initial
}

泛型结构体的"注意事项"

1
2
3
4
5
// ⚠️ 泛型结构体不支持递归类型别名
// type Bad[T any] = struct{ Value T }

// ⚠️ 泛型结构体的方法不能有泛型接收器之外的额外类型参数
// func (b Box[T]) Method[U any](x U) {}  // 编译错误!

泛型结构体的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ✅ 推荐:类型参数有实际意义
type Pair[K, V any] struct { K, V }
type Stack[T any] struct { items []T }

// ❌ 避免:类型参数过多
type TooMany[T, K, V, U, X any] struct{}  // 不推荐超过3个

// ✅ 使用:标准库风格命名
[K comparable, V any]
[Element any]

小剧场

3D打印机的故事:

设计师(开发者):“我设计了一个万能盒子模具(泛型结构体),可以打印任何材料的盒子!”

客户A:“我要一个塑料盒子!”

设计师:“没问题!用塑料(int? 不对,用具体材料)作为类型参数,打印!”

1
Box[Plastic]{content: myPlastic}

客户B:“我要一个金属盒子!”

设计师:“没问题!用金属作为类型参数,打印!”

1
Box[Metal]{content: myMetal}

客户C:“我要一个…思想盒子?”

设计师:“没问题!用interface{}…不对,用any作为类型参数,打印!”

1
Box[any]{content: myIdea}

客户:“哇!这个模具太万能了!”

设计师:“这就是泛型结构体的威力——一份设计,多种材料!”


33.3.2 泛型接口

泛型接口——带有类型参数的接口,让接口定义也能复用!这是Go泛型中最"高级"的特性之一。

1
2
3
4
5
6
// 泛型接口定义
type Container[T any] interface {
    Add(T)
    Get(int) T
    Size() int
}

泛型接口的"类型参数位置"

1
2
3
4
5
6
7
8
// 类型参数在接口名后面
type Adder[T any] interface {
    Add(T) T
}

type Comparer[T comparable] interface {
    Compare(T, T) int
}

泛型接口与泛型结构体的"配合"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 泛型接口
type Iterator[T any] interface {
    Next() (T, bool)
    Reset()
}

// 泛型结构体实现泛型接口
type SliceIterator[T any] struct {
    slice []T
    index int
}

func (s *SliceIterator[T]) Next() (T, bool) {
    if s.index >= len(s.slice) {
        return *new(T), false
    }
    val := s.slice[s.index]
    s.index++
    return val, true
}

func (s *SliceIterator[T]) Reset() {
    s.index = 0
}

// 使用
func Iterate[T any](iter Iterator[T]) {
    for {
        val, ok := iter.Next()
        if !ok {
            break
        }
        fmt.Println(val)
    }
}

泛型接口的"方法约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 泛型接口 + 方法约束
type Printable[T any] interface {
    Print()
    ~int | ~string | ~float64
}

func PrintAll[T Printable[T]](items []T) {
    for _, item := range items {
        item.Print()
    }
}

泛型接口的"嵌套使用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 泛型接口可以嵌套
type ReadWrite[T any] interface {
    Read(T) error
    Write(T) error
}

type Closeable[T any] interface {
    Close() error
}

type ReadWriteCloser[T any] interface {
    ReadWrite[T]
    Closeable[T]
}

泛型接口的"函数式风格"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 泛型Map函数接口
type Mapper[T, U any] interface {
    Map(T) U
}

// 泛型Filter函数接口
type Predicate[T any] interface {
    Test(T) bool
}

// 使用
func MapFilter[T, U any](items []T, mapper Mapper[T, U], pred Predicate[U]) []U {
    result := []U{}
    for _, item := range items {
        mapped := mapper.Map(item)
        if pred.Test(mapped) {
            result = append(result, mapped)
        }
    }
    return result
}

泛型接口的"实际应用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

// 泛型比较器接口
type Comparator[T any] interface {
    Less(T) bool
}

// 泛型排序器实现
type Sorter[T any] struct {
    items []T
    cmp   func(T, T) bool
}

func NewSorter[T any](items []T, cmp func(T, T) bool) *Sorter[T] {
    return &Sorter[T]{items: items, cmp: cmp}
}

func (s *Sorter[T]) Sort() {
    for i := 0; i < len(s.items)-1; i++ {
        for j := i + 1; j < len(s.items); j++ {
            if s.cmp(s.items[j], s.items[i]) {
                s.items[i], s.items[j] = s.items[j], s.items[i]
            }
        }
    }
}

func (s *Sorter[T]) Get() []T {
    return s.items
}

func main() {
    // 对整数排序
    intSorter := NewSorter([]int{5, 2, 8, 1, 9}, func(a, b int) bool {
        return a < b
    })
    intSorter.Sort()
    fmt.Println("Ints:", intSorter.Get()) // [1 2 5 8 9]

    // 对字符串排序
    strSorter := NewSorter([]string{"banana", "apple", "cherry"}, func(a, b string) bool {
        return a < b
    })
    strSorter.Sort()
    fmt.Println("Strings:", strSorter.Get()) // [apple banana cherry]
}

泛型接口的"约束作用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 泛型接口作为约束
type NumberArithmetic[T any] interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
    Add(T) T
    Subtract(T) T
    Multiply(T) T
}

func Sum[T NumberArithmetic[T]](values ...T) T {
    var result T
    for _, v := range values {
        result = result.Add(v)
    }
    return result
}

泛型接口的"注意事项"

1
2
3
4
5
6
7
8
9
// ⚠️ 泛型接口不能实现自身
// type Bad[T any] interface {
//     Bad[T]  // 编译错误!递归
// }

// ⚠️ 泛型接口的方法不能有额外的类型参数
// type Bad[T any] interface {
//     Method[T any](T)  // 编译错误!
// }

泛型接口的"最佳实践"

1
2
3
4
5
6
7
// ✅ 泛型接口适合用于:
// 1. 定义通用的算法接口
// 2. 定义容器接口
// 3. 定义函数式接口

// ❌ 避免过度使用泛型接口
// 简单的场景用普通接口+泛型函数更清晰

小剧场

乐高接口的故事:

乐高设计师(开发者):“我设计了一个’万能接口’,只要遵守这个接口的规则,任何乐高零件都可以插进来!”

接口定义:

1
2
3
4
type Connector[T any] interface {
    Connect(T)
    Disconnect(T) bool
}

正方形零件(int):“我会Connect和Disconnect!”

设计师:“好,你可以插入!”

圆形零件(string):“我也会!”

设计师:“好,你也可以插入!”

三角形零件(float64):“我也会Connect和Disconnect!”

设计师:“好!你也是合格的Connector!”

这就是泛型接口的作用——定义一个"标准",只要符合标准,任何类型都可以使用!

下一个小节预告:33.4 泛型函数——让函数更加通用!


33.4 泛型函数

泛型函数是Go泛型的核心,它让函数可以处理多种数据类型,而不需要为每种类型写一个重复的函数。

33.4.1 泛型函数声明

泛型函数声明——在函数名后面加上一对[],里面放类型参数,就构成了泛型函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 普通函数
func DoubleInt(x int) int {
    return x * 2
}

// 泛型函数
func Double[T int | float64](x T) T {
    return x * 2
}

// 调用
result1 := Double(5)    // Go推断T=int
result2 := Double(3.14) // Go推断T=float64

泛型函数的"标准格式"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 单类型参数
func Identity[T any](x T) T {
    return x
}

// 多类型参数
func MakePair[K, V any](k K, v V) struct{ K K; V V } {
    return struct{ K K; V V }{k, v}
}

// 带约束的类型参数
func Max[T int | string](a, b T) T {
    if a > b {
        return a
    }
    return b
}

泛型函数的"调用方式"

graph LR
    A["泛型函数调用"] --> B["显式指定类型"]
    A --> C["类型推断"]
    
    B --> B1["Double[int](5)"]
    C --> C1["Double(5) 自动推断"]
    
    style B fill:#87CEEB
    style C fill:#90EE90

泛型函数的"显式指定"

1
2
3
4
5
6
7
8
// 显式指定类型实参
result := Double[int](5)       // result是int类型
strResult := Max[string]("a", "b")  // strResult是string类型

// 当类型推断无法工作时,必须显式指定
var s []string
// Copy(s, "hello")  // 无法推断
Copy[string](s, "hello")  // 显式指定

泛型函数的"类型推断"

1
2
3
4
5
6
7
8
// Go根据参数推断类型
func Print[T any](value T) {
    fmt.Println(value)
}

Print(42)       // T推断为int
Print("hello")  // T推断为string
Print(3.14)     // T推断为float64

泛型函数的"多参数推断"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 多参数函数
func Min[T int | float64](a, b T) T {
    if a < b {
        return a
    }
    return b
}

Min(1, 2)       // T推断为int
Min(1.5, 2.5)   // T推断为float64

泛型函数的"返回值类型推断"

1
2
3
4
5
6
7
8
// 只有返回值时,无法推断参数类型
func Zero[T any]() T {
    return *new(T)
}

// 必须显式指定
var n int = Zero[int]()
var s string = Zero[string]()

泛型函数的"实际示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import "fmt"

// 交换函数
func Swap[T any](a, b T) (T, T) {
    return b, a
}

// 去重函数
func Unique[T comparable](items []T) []T {
    seen := make(map[T]bool)
    result := []T{}
    for _, item := range items {
        if !seen[item] {
            seen[item] = true
            result = append(result, item)
        }
    }
    return result
}

// 过滤函数
func Filter[T any](items []T, predicate func(T) bool) []T {
    result := []T{}
    for _, item := range items {
        if predicate(item) {
            result = append(result, item)
        }
    }
    return result
}

// Map函数
func Map[T, U any](items []T, transform func(T) U) []U {
    result := make([]U, len(items))
    for i, item := range items {
        result[i] = transform(item)
    }
    return result
}

func main() {
    // Swap
    a, b := Swap(1, 2)
    fmt.Println(a, b) // 2 1

    // Unique
    nums := []int{1, 2, 2, 3, 3, 3}
    fmt.Println(Unique(nums)) // [1 2 3]

    // Filter
    nums = []int{1, 2, 3, 4, 5, 6}
    evens := Filter(nums, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println(evens) // [2 4 6]

    // Map
    strs := []int{1, 2, 3}
    doubled := Map(strs, func(n int) int {
        return n * 2
    })
    fmt.Println(doubled) // [2 4 6]
}

泛型函数的"约束使用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 使用接口约束
type Stringer interface {
    String() string
}

func ToStrings[T Stringer](items []T) []string {
    result := make([]string, len(items))
    for i, item := range items {
        result[i] = item.String()
    }
    return result
}

type Person struct {
    Name string
}

func (p Person) String() string {
    return p.Name
}

func main() {
    people := []Person{{Name: "Alice"}, {Name: "Bob"}}
    names := ToStrings(people)
    fmt.Println(names) // [Alice Bob]
}

泛型函数的"注意事项"

1
2
3
4
5
6
// ⚠️ 泛型函数不能有同名非泛型函数(会冲突)
func F[T any](x T) {}
func F(x int) {}  // 编译错误!重复定义

// ⚠️ 泛型函数的类型推断有时会失败
// 需要显式指定类型

泛型函数的"最佳实践"

1
2
3
4
5
6
7
8
// ✅ 推荐:约束尽量精确
func Sum[T int | float64](values []T) T

// ✅ 推荐:类型推断优先
// 简单场景让Go推断,不需要显式指定

// ❌ 避免:过宽的约束
func F[T any](x T)  // 只在真正需要any时使用

小剧场

全能厨师的故事:

老板:“我们需要一位能做任何菜的厨师!”

普通厨师A:“我只会做川菜!”

普通厨师B:“我只会做粤菜!”

泛型厨师(泛型函数):“我什么菜都能做!只要告诉我用什么食材(类型参数),我就能做!”

老板:“太好了!来做个宫保鸡丁!”

泛型厨师:“没问题!(T=宫保鸡丁)”

老板:“再来个麻婆豆腐!”

泛型厨师:“没问题!(T=麻婆豆腐)”

老板:“太棒了!一位厨师(泛型函数),做出各种菜(不同类型)!”

这就是泛型函数的威力——一份代码,多种数据类型通用!


33.4.2 泛型方法

泛型方法——在泛型类型的方法上也可以使用类型参数,让方法更加灵活!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 泛型结构体
type Container[T any] struct {
    items []T
}

// 泛型方法
func (c *Container[T]) Map[U any](f func(T) U) []U {
    result := make([]U, len(c.items))
    for i, item := range c.items {
        result[i] = f(item)
    }
    return result
}

泛型方法的"类型参数位置"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 方法的类型参数在方法名之后
func (c *Container[T]) Process[U any](transform func(T) U) []U {
    // ...
}

//        ↑
//        方法名
//        ↓
// Container[T]
//        ↑
//        结构体的类型参数

泛型方法与普通方法的"区别"

特性普通方法泛型方法
接收器类型具体类型泛型类型
方法类型参数可以在方法上新增
调用方式普通调用可能需要显式指定

泛型方法的"多类型参数"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 结构体两个类型参数,方法可以新增或使用它们
type Pair[K, V any] struct {
    Key   K
    Value V
}

// 方法1:使用结构体的类型参数
func (p *Pair[K, V]) Swap() {
    p.Key, p.Value = p.Value, p.Key
}

// 方法2:引入新的类型参数
func (p *Pair[K, V]) MapKeys[U any](f func(K) U) *Pair[U, V] {
    return &Pair[U, V]{
        Key:   f(p.Key),
        Value: p.Value,
    }
}

// 方法3:既有结构体类型参数,也有方法类型参数
func (p *Pair[K, V]) Convert[K2, V2 any](fk func(K) K2, fv func(V) V2) *Pair[K2, V2] {
    return &Pair[K2, V2]{
        Key:   fk(p.Key),
        Value: fv(p.Value),
    }
}

泛型方法的"实际示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package main

import "fmt"

type Tree[T any] struct {
    value T
    left  *Tree[T]
    right *Tree[T]
}

// 中序遍历,返回[]T
func (t *Tree[T]) InOrder() []T {
    var result []T
    if t == nil {
        return result
    }
    result = append(result, t.left.InOrder()...)
    result = append(result, t.value)
    result = append(result, t.right.InOrder()...)
    return result
}

// 泛型方法:转换树中元素的类型
func (t *Tree[T]) Map[U any](f func(T) U) *Tree[U] {
    if t == nil {
        return nil
    }
    return &Tree[U]{
        value: f(t.value),
        left:  t.left.Map(f),
        right: t.right.Map(f),
    }
}

// 泛型方法:查找满足条件的元素
func (t *Tree[T]) Find(predicate func(T) bool) *T {
    if t == nil {
        return nil
    }
    if predicate(t.value) {
        return &t.value
    }
    if left := t.left.Find(predicate); left != nil {
        return left
    }
    return t.right.Find(predicate)
}

func main() {
    // 构建一个整数树
    root := &Tree[int]{
        value: 5,
        left: &Tree[int]{
            value: 3,
            left:  &Tree[int]{value: 1},
            right: &Tree[int]{value: 4},
        },
        right: &Tree[int]{
            value: 7,
            right: &Tree[int]{value: 9},
        },
    }

    // 中序遍历
    fmt.Println("InOrder:", root.InOrder()) // [1 3 4 5 7 9]

    // 转换为字符串树
    strTree := root.Map(func(n int) string {
        return fmt.Sprintf("num-%d", n)
    })
    fmt.Println("StrTree InOrder:", strTree.InOrder()) // [num-1 num-3 num-4 num-5 num-7 num-9]

    // 查找大于5的元素
    found := root.Find(func(n int) bool {
        return n > 5
    })
    if found != nil {
        fmt.Println("Found:", *found) // 7
    }
}

泛型方法的"链式调用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import "fmt"

type Query[T any] struct {
    data []T
}

// 过滤
func (q *Query[T]) Where(predicate func(T) bool) *Query[T] {
    result := []T{}
    for _, item := range q.data {
        if predicate(item) {
            result = append(result, item)
        }
    }
    return &Query[T]{data: result}
}

// 转换
func (q *Query[T]) Select[U any](transform func(T) U) *Query[U] {
    result := make([]U, len(q.data))
    for i, item := range q.data {
        result[i] = transform(item)
    }
    return &Query[U]{data: result}
}

// 取前n个
func (q *Query[T]) Take(n int) *Query[T] {
    if n >= len(q.data) {
        return q
    }
    return &Query[T]{data: q.data[:n]}
}

// 获取结果
func (q *Query[T]) ToSlice() []T {
    return q.data
}

type User struct {
    Name string
    Age  int
}

func main() {
    users := []User{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 35},
        {"David", 28},
    }

    names := (&Query[User]{data: users}).
        Where(func(u User) bool { return u.Age > 27 }).
        Select(func(u User) string { return u.Name }).
        Take(2).
        ToSlice()

    fmt.Println(names) // [Bob David]
}

泛型方法的"方法字典"概念

graph LR
    A["泛型方法"] --> B["泛型结构体"]
    A --> C["方法字典"]
    
    B --> D["泛型结构体有一个类型参数T"]
    C --> E["每种T都会生成一份方法实现"]
    
    style A fill:#FFD700

泛型方法的"注意事项"

1
2
3
4
5
6
7
8
9
// ⚠️ 方法不能有独立的类型参数(除了接收器已有的)
// ❌ 错误写法:
func (t *Tree[T]) BadMethod[U any](x U) {}  // 编译错误!

// ✅ 正确写法:使用接收器的类型参数
func (t *Tree[T]) GoodMethod[U any](f func(T) U) *Tree[U] {}

// ⚠️ 泛型方法的类型推断有时需要显式指定
// tree.Map[string](transformFn)

泛型方法的"最佳实践"

1
2
3
4
5
6
7
// ✅ 推荐:方法泛型参数少于3个
// 过多的类型参数会让代码难以理解

// ✅ 推荐:方法泛型参数最好只用于返回值
func (c *Container[T]) Find[U any](pred func(T) bool) *U

// ❌ 避免:过于复杂的方法签名

小剧场

变形金刚的故事:

汽车人(泛型方法):“我是一种特殊的汽车人,我的身体(结构体)可以变形,但我还能使用不同的武器(方法类型参数)!”

擎天柱(Tree[int]):“我是整数树!”

威震天(Tree[string]):“我是字符串树!”

威震天使用"变换"方法(Map[string, int]): “我要把自己从字符串树变成整数树!”

擎天柱:“那我也用变换方法(Map[int, string])把自己变成字符串树!”

两人:“同样的方法签名,不同的类型参数,不同的变换结果!”

这就是泛型方法的威力——同一个方法,不同的类型,产生不同的结果!

下一个小节预告:33.5 类型推断——让Go帮你"猜"类型!


33.5 类型推断

类型推断是Go泛型的"智能助手",它能根据你传入的参数自动"猜"出类型参数应该是什么,让你的代码更加简洁。

33.5.1 函数参数推断

函数参数推断——Go会根据你传入的参数类型来推断泛型函数的类型参数。

1
2
3
4
5
6
7
8
// 泛型函数
func Print[T any](value T) {
    fmt.Println(value)
}

// 调用时,Go自动推断T
Print(42)       // T推断为int
Print("hello")  // T推断为string

参数推断的"原理"

graph LR
    A["Print(42)"] --> B["Go发现参数是int"]
    B --> C["T=int"]
    C --> D["调用int版本的Print"]
    
    style A fill:#87CEEB
    style D fill:#90EE90

参数推断的"单参数推断"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func First[T any](slice []T) T {
    return slice[0]
}

// T从切片元素类型推断
nums := []int{1, 2, 3}
strs := []string{"a", "b", "c"}

fmt.Println(First(nums))  // T推断为int
fmt.Println(First(strs))  // T推断为string

参数推断的"多参数推断"

1
2
3
4
5
6
7
8
func Pair[K, V any](k K, v V) struct{ K K; V V } {
    return struct{ K K; V V }{k, v}
}

// K和V从两个参数推断
p1 := Pair(1, "one")     // K=int, V=string
p2 := Pair("key", 100)   // K=string, V=int
p3 := Pair(3.14, true)  // K=float64, V=bool

参数推断的"约束推断"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func Max[T int | string](a, b T) T {
    if a > b {
        return a
    }
    return b
}

// 从参数推断T
Max(1, 2)        // T=int
Max("a", "b")   // T=string
// Max(1, "a")   // 编译错误!参数类型不一致

参数推断的"失败情况"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 只有返回值,没有参数
func Zero[T any]() T {
    return *new(T)
}

// 无法推断T,必须显式指定
var n int = Zero[int]()
var s string = Zero[string]()

// 或者用变量声明
var zeroInt int = Zero()  // 编译错误!

参数推断的"复杂场景"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func Map[T, U any](items []T, transform func(T) U) []U {
    result := make([]U, len(items))
    for i, item := range items {
        result[i] = transform(item)
    }
    return result
}

// T从切片推断,U从函数返回值推断
nums := []int{1, 2, 3}
strs := Map(nums, func(n int) string {
    return fmt.Sprintf("num-%d", n)
})
// nums是[]int,所以T=int
// transform返回string,所以U=string

参数推断的"方法接收器"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Container[T any] struct {
    items []T
}

func (c *Container[T]) Get(index int) T {
    return c.items[index]
}

container := &Container[int]{items: []int{1, 2, 3}}
n := container.Get(0)  // T从Container[int]推断为int

参数推断的"最佳实践"

1
2
3
4
5
6
7
8
// ✅ 尽量让Go推断类型
result := Unique([]int{1, 2, 2, 3})  // 自动推断

// ✅ 需要时显式指定
var empty StringSlice = Unique[string]()  // 必须指定

// ❌ 避免过度依赖推断
// 在复杂场景下,显式指定可以避免歧义

参数推断的"注意事项"

1
2
3
4
5
6
7
// ⚠️ 如果多个参数的类型不一致,但都属于约束范围
// Go会尝试统一,如果无法统一则报错
func F[T int | string](a, b T) {}

F(1, "a")  // 编译错误!a和b类型不一致

// ⚠️ 方法接收器的类型推断有时需要上下文

小剧场

智能点餐机的故事:

顾客:“我要一份汉堡!”

点餐机(类型推断):“好的,我推断您要的是汉堡套餐(T=汉堡)!”

顾客:“我要一份薯条!”

点餐机:“好的,我推断您要的是薯条套餐(T=薯条)!”

顾客:“我要…等等,你不是说T是什么就是什么吗?那我要一份可乐加薯条套餐(T=?)!”

点餐机:“抱歉,两个不同类型的参数(汉堡和薯条),我无法统一推断T是什么类型。请您明确告诉我T是什么!”

顾客:“那我要T=套餐!”

点餐机:“好的!给您套餐!”

这就是参数推断——Go会根据参数帮你推断类型,但遇到复杂情况时,还是需要你显式指定!


33.5.2 约束类型推断

约束类型推断——Go不仅能从参数推断类型,还能根据约束(constraint)来帮助推断和验证类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 定义一个约束
type Number interface {
    int | int32 | int64 | float32 | float64
}

// 使用约束进行推断
func Sum[T Number](values ...T) T {
    var sum T
    for _, v := range values {
        sum += v  // T可以是int或float64
    }
    return sum
}

Sum(1, 2, 3)       // T=int
Sum(1.1, 2.2, 3.3) // T=float64

约束推断的"类型降级"

1
2
3
4
5
6
7
8
9
// ~int 约束允许自定义int类型
type MyInt int

func Double[T ~int](v T) T {
    return v * 2
}

Double(MyInt(5))  // T=MyInt
Double(10)        // T=int

约束推断的"方法约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 约束要求类型实现特定方法
type Stringer interface {
    String() string
}

func Join[T Stringer](items []T, sep string) string {
    if len(items) == 0 {
        return ""
    }
    result := items[0].String()
    for _, item := range items[1:] {
        result += sep + item.String()
    }
    return result
}

type Person struct {
    Name string
}

func (p Person) String() string {
    return p.Name
}

func main() {
    people := []Person{{"Alice"}, {"Bob"}, {"Charlie"}}
    fmt.Println(Join(people, ", ")) // Alice, Bob, Charlie
}

约束推断的"结构约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ~T 可以推断底层类型
type UserID int

func HashID[T ~int](id T) int {
    h := 0
    for _, c := range fmt.Sprintf("%d", int(id)) {
        h = h*31 + int(c)
    }
    return h
}

// T被推断为UserID
uid := UserID(12345)
fmt.Println(HashID(uid)) // 可以调用

约束推断的"组合约束"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 组合多个约束
type Signed interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
}

type Number interface {
    Signed | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

func Abs[T Number](v T) T {
    if v < 0 {
        return -v  // 需要T支持<操作
    }
    return v
}

约束推断的"自动解包"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 当约束是联合类型时,Go会处理类型匹配
type IntOrString interface {
    int | string
}

func Process[T IntOrString](v T) {
    switch any(v).(type) {
    case int:
        fmt.Println("int:", v)
    case string:
        fmt.Println("string:", v)
    }
}

Process(42)       // int: 42
Process("hello")  // string: hello

约束推断的"最佳实践"

1
2
3
4
5
6
7
8
// ✅ 使用约束让代码更精确
// 约束越精确,Go推断越准确

// ✅ 优先使用标准库约束
// constraints.Ordered, constraints.Integer等

// ❌ 避免过于宽泛的约束
// func F[T any](x T) {}  // 太宽泛了

约束推断的"类型推断流程"

graph TD
    A["类型推断流程"] --> B["1. 从参数推断"]
    B --> C["2. 从返回值推断"]
    C --> D["3. 从约束推断"]
    D --> E["4. 验证类型满足约束"]
    E --> F{"通过?"}
    F -->|是| G["编译通过"]
    F -->|否| H["编译错误"]
    
    style F fill:#FFD700

小剧场

密码锁的故事:

密码锁(约束推断):“这个锁(约束)需要4位数字(~int),但支持自定义数字(自定义int类型)!”

小明:“我要设密码1234!”

锁:“好的,1234是int类型(int),验证通过!”

小红:“我要设密码,我的学号是MyInt(2024001)!”

锁:“好的,MyInt底层是int(~int),验证通过!”

小刚:“我要设密码ABC123!”

锁:“抱歉,ABC123是string类型,不在~int约束范围内!”

这就是约束类型推断——不仅推断类型,还验证类型是否满足约束!

下一个小节预告:33.6 泛型约束设计——如何设计好的约束!


33.6 泛型约束设计

好的约束设计是泛型编程的关键。这一节我们将学习如何设计出既灵活又安全的约束。

33.6.1 约束组合

约束组合——将多个约束合并成一个,让泛型代码既能满足功能需求,又能保证类型安全。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 组合多个约束
type SignedNumber interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
}

type UnsignedNumber interface {
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

// 组合为数值约束
type Number interface {
    SignedNumber | UnsignedNumber
}

约束组合的"叠加原理"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 基础类型 + 方法约束
type Comparable[T any] interface {
    comparable
    Less(T) bool
}

type Printable[T any] interface {
    comparable
    String() string
}

// 组合两个约束
type SortedItem[T any] interface {
    Comparable[T]
    Printable[T]
}

约束组合的"交集与并集"

graph LR
    A["约束A: int|string"] --> C["并集: A|B = int|string|float64"]
    B["约束B: float64|string"] --> C
    
    A --> D["交集: A&B = string"]
    B --> D
    
    style C fill:#90EE90
    style D fill:#87CEEB

约束组合的"实际应用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main

import "fmt"

// 数值类型
type Numeric interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64
}

// 可比较类型
type Ordered interface {
    Numeric | ~string | ~bool
}

// 泛型函数使用组合约束
func Min[T Ordered](a, b T) T {
    if a < b {
        return a
    }
    return b
}

func Max[T Ordered](a, b T) T {
    if a > b {
        return a
    }
    return b
}

func main() {
    fmt.Println(Min(1, 2))           // 1
    fmt.Println(Max(1.5, 2.5))      // 2.5
    fmt.Println(Min("apple", "banana")) // apple
    fmt.Println(Max(true, false))    // true
}

约束组合的"标准库示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Go标准库的constraints包
package constraints

// Ordered定义了可排序的类型
type Ordered interface {
    Integer | Float | ~string | ~bool
}

// Integer定义了整数类型
type Integer interface {
    Signed | Unsigned
}

// Signed定义了有符号整数
type Signed interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

// Unsigned定义了无符号整数
type Unsigned interface {
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

// Float定义了浮点数
type Float interface {
    ~float32 | ~float64
}

约束组合的"方法约束组合"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义基础方法约束
type Adder[T any] interface {
    Add(T) T
}

type Subber[T any] interface {
    Sub(T) T
}

type Negator[T any] interface {
    Neg() T
}

// 组合为计算器约束
type Calculator[T any] interface {
    Adder[T]
    Subber[T]
    Negator[T]
}

// 所有类型参数的方法可以链式调用
func Compute[T Calculator[T]](a, b T) T {
    return a.Add(b).Sub(b).Neg()
}

约束组合的"最佳实践"

1
2
3
4
5
6
7
8
// ✅ 约束应该精确且完整
// 只包含需要的约束,不要多余

// ✅ 约束应该可组合
// 将复杂约束拆分为简单约束

// ❌ 避免过于复杂的约束组合
// 超过3-4个约束的组合会让代码难以理解

小剧场

乐高积木的故事:

基础积木(基础约束):

  • 红色积木(int)
  • 蓝色积木(string)

高级积木(组合约束):

  • 红色积木(int)+ 蓝色积木(string)= 彩色积木(int | string)

玩家:“我需要既能和红色积木组合(~int),又能和蓝色积木组合(~string)的高级积木!”

设计师:“没问题!这是我们的组合约束积木(Numeric | String)!”

这就是约束组合的威力——把简单约束组合成复杂约束!


33.6.2 约束推导

约束推导——Go编译器能自动从代码中的操作推导出类型参数需要满足的约束,让你写更少的约束声明。

1
2
3
4
5
6
7
8
9
// Go会自动推导T需要支持 < 操作
func Min[T any](a, b T) T {
    if a < b {  // Go看到a和b使用了<操作
        return a
    }
    return b
}

// Go自动将约束设为comparable

约束推导的"自动检测"

graph LR
    A["代码中的操作"] --> B["Go分析需要的操作"]
    B --> C["自动推导约束"]
    C --> D["T comparable"]
    
    style A fill:#87CEEB
    style D fill:#90EE90

约束推导的"自动推断示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import "fmt"

// Go自动推导出T必须是可比较的
func Contains[T any](slice []T, target T) bool {
    for _, item := range slice {
        if item == target {  // 使用了==操作
            return true
        }
    }
    return false
}

// Go自动推导出T必须可以用fmt.Println打印
func PrintAll[T any](items []T) {
    for _, item := range items {
        fmt.Println(item)  // 需要T可以被打印
    }
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    fmt.Println(Contains(nums, 3))  // true
    fmt.Println(Contains(nums, 10)) // false
}

约束推导的"方法调用推导"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Go从方法调用推导出类型约束
func FirstN[T any](s []T, n int) []T {
    result := []T{}
    for i := 0; i < n && i < len(s); i++ {
        result = append(result, s[i])
    }
    return result
}

// Go不知道slice的类型约束
// 但知道需要能够切片索引

约束推导的"限制"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ⚠️ Go无法从所有操作推导约束
// 例如:数学运算需要明确约束

func Double[T any](v T) T {
    return v * 2  // 编译错误!T是any,不知道如何相乘
}

// 必须明确约束
func Double[T int | float64](v T) T {
    return v * 2  // works
}

约束推导的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// ✅ 当操作需要特定约束时,让Go自动推导
func Unique[T comparable](items []T) []T  // 自动推导

// ✅ 当需要特定操作时,明确声明约束
func Double[T int | float64](v T) T  // 需要明确声明

// ✅ 结合使用
func Max[T int | float64 | string](a, b T) T {
    if a > b {
        return a
    }
    return b
}

小剧场

智能翻译机的故事:

翻译机(编译器):“我看到你说’你好’,我推断你用的是中文!”

用户:“没错!”

翻译机:“我看到你说'2 + 2’,我推断你需要数学运算功能!”

用户:“没错!”

翻译机:“我看到你说’你好’ + ‘世界’,这我就推断不了了,这是字符串拼接还是其他操作?我需要一个明确的约束!”

用户:“那是因为你需要在约束中明确声明支持的运算!”

这就是约束推导——Go能自动推断的就不用声明,不能推断的必须明确!


33.6.3 标准库约束

标准库约束——Go标准库的constraints包提供了常用的约束定义,让你的泛型代码更加简洁和规范。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import "golang.org/x/exp/constraints"

// constraints.Ordered - 可排序的类型
func Min[T constraints.Ordered](a, b T) T {
    if a < b {
        return a
    }
    return b
}

// constraints.Integer - 所有整数类型
// constraints.Float - 所有浮点类型
// constraints.Signed - 所有有符号整数
// constraints.Unsigned - 所有无符号整数

标准库约束的"种类"

约束含义示例类型
Ordered可比较大小int, float, string, bool
Integer所有整数int, uint, rune, byte
Float所有浮点数float32, float64
Signed有符号整数int, int8, int16, int32, int64
Unsigned无符号整数uint, uint8, uint16, uint32, uint64, uintptr
Complex复数类型complex64, complex128

constraints.Ordered的"详细定义"

1
2
3
4
// Ordered 包含可排序的类型
type Ordered interface {
    Integer | Float | ~string | ~bool
}

使用标准库约束的"示例"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

// 求和 - 使用Ordered
func Sum[T constraints.Ordered](values []T) T {
    var sum T
    for _, v := range values {
        sum += v
    }
    return sum
}

// 求最大值 - 使用Ordered
func Max[T constraints.Ordered](values ...T) T {
    if len(values) == 0 {
        return *new(T)
    }
    max := values[0]
    for _, v := range values[1:] {
        if v > max {
            max = v
        }
    }
    return max
}

// 求最小值 - 使用Ordered
func Min[T constraints.Ordered](values ...T) T {
    if len(values) == 0 {
        return *new(T)
    }
    min := values[0]
    for _, v := range values[1:] {
        if v < min {
            min = v
        }
    }
    return min
}

// 绝对值 - 使用Signed
func Abs[T constraints.Signed](v T) T {
    if v < 0 {
        return -v
    }
    return v
}

// 检查是否为零 - 使用Integer
func IsZero[T constraints.Integer](v T) bool {
    return v == 0
}

func main() {
    fmt.Println(Sum([]int{1, 2, 3, 4, 5}))      // 15
    fmt.Println(Sum([]float64{1.1, 2.2, 3.3}))  // 6.6
    fmt.Println(Max(1, 5, 3, 9, 2))             // 9
    fmt.Println(Min(1, 5, 3, 9, 2))             // 1
    fmt.Println(Max("apple", "banana"))          // banana
    fmt.Println(Abs(-42))                        // 42
    fmt.Println(IsZero(0))                       // true
}

标准库约束的"自定义扩展"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 可以基于标准库约束创建自定义约束
type MyNumeric interface {
    constraints.Integer | constraints.Float
}

// 添加自定义类型
type MyInt int

func Double[T MyNumeric](v T) T {
    return v * 2
}

func main() {
    fmt.Println(Double(42))        // 84
    fmt.Println(Double(3.14))     // 6.28
    fmt.Println(Double(MyInt(5))) // 10
}

标准库约束的"位置"

1
2
3
4
5
# constraints包在 golang.org/x/exp/constraints
# 从Go 1.21开始,标准库也有部分约束

# 安装constraints包
go get golang.org/x/exp/constraints

标准库约束的"注意事项"

1
2
3
4
5
// ⚠️ constraints包在 golang.org/x/exp
// 不是标准库的一部分,但官方维护

// ⚠️ 包名是 constraints,不是 constraint
import "golang.org/x/exp/constraints"

标准库约束的"最佳实践"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// ✅ 优先使用标准库约束
func F[T constraints.Ordered](v T) {}

// ✅ 自定义约束时基于标准库约束
type MyOrdered interface {
    constraints.Ordered | ~[]byte
}

// ❌ 不要重复发明轮子
// 自己定义 int | string | ... 之前先检查标准库是否已有

小剧场

超市的故事:

顾客:“我要买一些可以吃的东西(constraints.Ordered)!”

超市:“好的,我们有水果、蔬菜、肉类…这些都是可以吃的!”

顾客:“我要买一些数字类型的东西(constraints.Integer)!”

超市:“好的,我们有苹果、香蕉…等等,数字类型?”

顾客:“啊,不是超市,是constraints包里的定义!”

超市:“那你应该去编程的世界买!”

constraints包就是这样——提供了常用的"分类定义",让你的代码更加简洁!

下一个小节预告:33.7 泛型模式——泛型的"套路"总结!


33.7 泛型模式

泛型编程有很多经典的"套路",掌握这些模式能让你写出更加优雅的泛型代码。

33.7.1 泛型数据结构

泛型数据结构——用泛型实现的常用数据结构,如栈、队列、链表、树等,可以服务于任意数据类型。

33.7.1.1 泛型栈

泛型栈——后进先出(LIFO)的数据结构,用泛型实现可以存储任意类型的元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main

import "fmt"

// 泛型栈
type Stack[T any] struct {
    items []T
    top   int
}

// 创建新栈
func NewStack[T any]() *Stack[T] {
    return &Stack[T]{items: make([]T, 0), top: -1}
}

// 入栈
func (s *Stack[T]) Push(item T) {
    s.items = append(s.items, item)
    s.top++
}

// 出栈
func (s *Stack[T]) Pop() (T, bool) {
    if s.top < 0 {
        return *new(T), false
    }
    item := s.items[s.top]
    s.items = s.items[:s.top]
    s.top--
    return item, true
}

// 查看栈顶
func (s *Stack[T]) Peek() (T, bool) {
    if s.top < 0 {
        return *new(T), false
    }
    return s.items[s.top], true
}

// 栈是否为空
func (s *Stack[T]) IsEmpty() bool {
    return s.top < 0
}

// 栈大小
func (s *Stack[T]) Size() int {
    return s.top + 1
}

func main() {
    // 整数栈
    intStack := NewStack[int]()
    intStack.Push(1)
    intStack.Push(2)
    intStack.Push(3)

    fmt.Println("Int Stack:")
    fmt.Println("Size:", intStack.Size())   // 3
    fmt.Println("Peek:", intStack.Peek())   // 3
    fmt.Println("Pop:", intStack.Pop())     // 3 true
    fmt.Println("Pop:", intStack.Pop())     // 2 true
    fmt.Println("Pop:", intStack.Pop())     // 1 true
    fmt.Println("Pop:", intStack.Pop())     // 0 false

    // 字符串栈
    strStack := NewStack[string]()
    strStack.Push("hello")
    strStack.Push("world")

    fmt.Println("\nString Stack:")
    fmt.Println("Pop:", strStack.Pop())     // world true
}

栈的"应用场景"

graph LR
    A["栈的应用场景"] --> B["表达式求值"]
    A --> C["函数调用栈"]
    A --> D["括号匹配"]
    A --> E["撤销操作"]
    
    style A fill:#FFD700

33.7.1.2 泛型队列

泛型队列——先进先出(FIFO)的数据结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 泛型队列
type Queue[T any] struct {
    items []T
    front int
    rear  int
    size  int
}

// 创建新队列
func NewQueue[T any]() *Queue[T] {
    return &Queue[T]{
        items: make([]T, 10),
        front: 0,
        rear:  -1,
        size:  0,
    }
}

// 入队
func (q *Queue[T]) Enqueue(item T) {
    if q.size == cap(q.items) {
        q.resize()
    }
    q.rear = (q.rear + 1) % cap(q.items)
    q.items[q.rear] = item
    q.size++
}

// 出队
func (q *Queue[T]) Dequeue() (T, bool) {
    if q.size == 0 {
        return *new(T), false
    }
    item := q.items[q.front]
    q.front = (q.front + 1) % cap(q.items)
    q.size--
    return item, true
}

// 调整大小
func (q *Queue[T]) resize() {
    newItems := make([]T, cap(q.items)*2)
    for i := 0; i < q.size; i++ {
        newItems[i] = q.items[(q.front+i)%cap(q.items)]
    }
    q.items = newItems
    q.front = 0
    q.rear = q.size - 1
}

队列的"应用场景"

graph LR
    A["队列的应用场景"] --> B["任务调度"]
    A --> C["广度优先搜索"]
    A --> D["消息队列"]
    A --> E["打印队列"]
    
    style A fill:#FFD700

33.7.1.3 泛型链表

泛型链表——一种基本的线性数据结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 链表节点
type ListNode[T any] struct {
    value T
    next  *ListNode[T]
}

// 链表
type LinkedList[T any] struct {
    head *ListNode[T]
    tail *ListNode[T]
    size int
}

// 添加到尾部
func (l *LinkedList[T]) Append(value T) {
    node := &ListNode[T]{value: value}
    if l.tail == nil {
        l.head = node
        l.tail = node
    } else {
        l.tail.next = node
        l.tail = node
    }
    l.size++
}

// 添加到头部
func (l *LinkedList[T]) Prepend(value T) {
    node := &ListNode[T]{value: value, next: l.head}
    l.head = node
    if l.tail == nil {
        l.tail = node
    }
    l.size++
}

// 获取元素
func (l *LinkedList[T]) Get(index int) (T, bool) {
    if index < 0 || index >= l.size {
        return *new(T), false
    }
    current := l.head
    for i := 0; i < index; i++ {
        current = current.next
    }
    return current.value, true
}

// 删除元素
func (l *LinkedList[T]) Remove(index int) bool {
    if index < 0 || index >= l.size {
        return false
    }
    if index == 0 {
        l.head = l.head.next
        if l.head == nil {
            l.tail = nil
        }
    } else {
        prev := l.head
        for i := 0; i < index-1; i++ {
            prev = prev.next
        }
        prev.next = prev.next.next
        if index == l.size-1 {
            l.tail = prev
        }
    }
    l.size--
    return true
}

33.7.1.4 泛型树

泛型树——泛型的树结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 二叉树节点
type TreeNode[T any] struct {
    value T
    left  *TreeNode[T]
    right *TreeNode[T]
}

// 二叉搜索树
type BST[T any] struct {
    root   *TreeNode[T]
    less   func(a, b T) bool
}

// 创建BST
func NewBST[T any](less func(a, b T) bool) *BST[T] {
    return &BST[T]{less: less}
}

// 插入
func (b *BST[T]) Insert(value T) {
    node := &TreeNode[T]{value: value}
    if b.root == nil {
        b.root = node
        return
    }
    current := b.root
    for {
        if b.less(value, current.value) {
            if current.left == nil {
                current.left = node
                return
            }
            current = current.left
        } else {
            if current.right == nil {
                current.right = node
                return
            }
            current = current.right
        }
    }
}

// 中序遍历
func (b *BST[T]) InOrder() []T {
    var result []T
    var traverse func(*TreeNode[T])
    traverse = func(n *TreeNode[T]) {
        if n == nil {
            return
        }
        traverse(n.left)
        result = append(result, n.value)
        traverse(n.right)
    }
    traverse(b.root)
    return result
}

func main() {
    bst := NewBST[int](func(a, b int) bool { return a < b })
    bst.Insert(5)
    bst.Insert(3)
    bst.Insert(7)
    bst.Insert(1)
    bst.Insert(4)

    fmt.Println("InOrder:", bst.InOrder()) // [1 3 4 5 7]
}

树的"应用场景"

graph LR
    A["树的应用场景"] --> B["文件系统"]
    A --> C["DOM树"]
    A --> D["组织结构"]
    A --> E["搜索优化"]
    
    style A fill:#FFD700

下一个小节预告:33.7.2 泛型算法——通用的算法实现!


33.7.2 泛型算法

泛型算法是用泛型实现的通用算法,可以作用于各种数据类型。

33.7.2.1 泛型排序

泛型排序——用泛型实现的各种排序算法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import "fmt"

// 泛型冒泡排序
func BubbleSort[T any](arr []T, less func(a, b T) bool) {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        swapped := false
        for j := 0; j < n-i-1; j++ {
            if less(arr[j+1], arr[j]) {
                arr[j], arr[j+1] = arr[j+1], arr[j]
                swapped = true
            }
        }
        if !swapped {
            break
        }
    }
}

// 泛型快速排序
func QuickSort[T any](arr []T, less func(a, b T) bool) {
    quickSortHelper(arr, 0, len(arr)-1, less)
}

func quickSortHelper[T any](arr []T, low, high int, less func(a, b T) bool) {
    if low < high {
        pi := partition(arr, low, high, less)
        quickSortHelper(arr, low, pi-1, less)
        quickSortHelper(arr, pi+1, high, less)
    }
}

func partition[T any](arr []T, low, high int, less func(a, b T) bool) int {
    pivot := arr[high]
    i := low - 1
    for j := low; j < high; j++ {
        if less(pivot, arr[j]) {
            i++
            arr[i], arr[j] = arr[j], arr[i]
        }
    }
    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i + 1
}

func main() {
    // 整数排序
    ints := []int{64, 34, 25, 12, 22, 11, 90}
    fmt.Println("Before sort:", ints)
    BubbleSort(ints, func(a, b int) bool { return a > b })
    fmt.Println("After bubble sort:", ints)

    // 用QuickSort
    ints2 := []int{64, 34, 25, 12, 22, 11, 90}
    QuickSort(ints2, func(a, b int) bool { return a > b })
    fmt.Println("After quick sort:", ints2)

    // 字符串排序
    strs := []string{"banana", "apple", "cherry", "date"}
    fmt.Println("\nBefore sort:", strs)
    QuickSort(strs, func(a, b string) bool { return a > b })
    fmt.Println("After sort:", strs)
}

排序算法的复杂度

算法平均时间最坏时间空间
冒泡排序O(n²)O(n²)O(1)
快速排序O(n log n)O(n²)O(log n)
归并排序O(n log n)O(n log n)O(n)

33.7.2.2 泛型查找

泛型查找——二分查找等通用查找算法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 二分查找(需要有序切片)
func BinarySearch[T any](arr []T, target T, compare func(a, b T) int) int {
    left, right := 0, len(arr)-1
    for left <= right {
        mid := left + (right-left)/2
        cmp := compare(arr[mid], target)
        if cmp == 0 {
            return mid
        } else if cmp < 0 {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }
    return -1
}

// 比较函数返回:
// < 0: a < b
// = 0: a == b
// > 0: a > b

func main() {
    // 整数二分查找
    ints := []int{1, 3, 5, 7, 9, 11, 13}
    idx := BinarySearch(ints, 7, func(a, b int) int { return a - b })
    fmt.Println("Found at index:", idx) // 3

    // 字符串二分查找
    strs := []string{"apple", "banana", "cherry", "date"}
    idx2 := BinarySearch(strs, "cherry", func(a, b string) int {
        if a < b {
            return -1
        } else if a > b {
            return 1
        }
        return 0
    })
    fmt.Println("Found at index:", idx2) // 2
}

33.7.2.3 泛型过滤

泛型过滤——按条件筛选元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 泛型过滤
func Filter[T any](arr []T, predicate func(T) bool) []T {
    result := []T{}
    for _, item := range arr {
        if predicate(item) {
            result = append(result, item)
        }
    }
    return result
}

// 带索引的过滤
func FilterWithIndex[T any](arr []T, predicate func(int, T) bool) []T {
    result := []T{}
    for i, item := range arr {
        if predicate(i, item) {
            result = append(result, item)
        }
    }
    return result
}

func main() {
    // 过滤偶数
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    evens := Filter(nums, func(n int) bool { return n%2 == 0 })
    fmt.Println("Evens:", evens) // [2 4 6 8 10]

    // 过滤长度大于3的字符串
    strs := []string{"go", "rust", "java", "python", "js"}
    longStrs := Filter(strs, func(s string) bool { return len(s) > 3 })
    fmt.Println("Long strings:", longStrs) // [rust java python]

    // 带索引过滤
    idxFiltered := FilterWithIndex(nums, func(i, n int) bool { return i%2 == 0 })
    fmt.Println("Even indices:", idxFiltered) // [1 3 5 7 9]
}

33.7.2.4 泛型映射

泛型映射——对每个元素进行转换。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 泛型Map
func Map[T, U any](arr []T, transform func(T) U) []U {
    result := make([]U, len(arr))
    for i, item := range arr {
        result[i] = transform(item)
    }
    return result
}

// 带索引的Map
func MapWithIndex[T, U any](arr []T, transform func(int, T) U) []U {
    result := make([]U, len(arr))
    for i, item := range arr {
        result[i] = transform(i, item)
    }
    return result
}

// 扁平化Map
func FlatMap[T, U any](arr []T, transform func(T) []U) []U {
    result := []U{}
    for _, item := range arr {
        result = append(result, transform(item)...)
    }
    return result
}

func main() {
    // 整数转字符串
    nums := []int{1, 2, 3, 4, 5}
    strs := Map(nums, func(n int) string {
        return fmt.Sprintf("num-%d", n)
    })
    fmt.Println("Mapped:", strs) // [num-1 num-2 num-3 num-4 num-5]

    // 带索引转换
    indexed := MapWithIndex(nums, func(i, n int) string {
        return fmt.Sprintf("[%d]=%d", i, n)
    })
    fmt.Println("Indexed:", indexed) // [0]=1 [1]=2 [2]=3 [3]=4 [4]=5

    // 扁平化
    nested := [][]int{{1, 2}, {3, 4}, {5, 6}}
    flat := FlatMap(nested, func(arr []int) []int {
        return arr
    })
    fmt.Println("FlatMap:", flat) // [1 2 3 4 5 6]
}

下一个小节预告:33.7.3 泛型函数式编程——函数式风格的泛型实现!


33.7.3 泛型函数式编程

函数式编程风格和泛型结合,让代码更加声明式和简洁。

33.7.3.1 泛型 Map 函数

泛型 Map——将一个切片的每个元素映射为另一个值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import "fmt"

// Map 将T类型切片转换为U类型切片
func Map[T, U any](items []T, fn func(T) U) []U {
    result := make([]U, len(items))
    for i, item := range items {
        result[i] = fn(item)
    }
    return result
}

// MapPtr 将T类型切片原地映射(如果U=T)
func MapPtr[T any](items []T, fn func(*T)) {
    for i := range items {
        fn(&items[i])
    }
}

// MapIdx 带索引的Map
func MapIdx[T, U any](items []T, fn func(int, T) U) []U {
    result := make([]U, len(items))
    for i, item := range items {
        result[i] = fn(i, item)
    }
    return result
}

type User struct {
    Name string
    Age  int
}

func main() {
    // 整数转字符串
    nums := []int{1, 2, 3, 4, 5}
    strs := Map(nums, func(n int) string {
        return fmt.Sprintf("num-%d", n)
    })
    fmt.Println("Int to String:", strs)
    // Output: [num-1 num-2 num-3 num-4 num-5]

    // 用户转用户名
    users := []User{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 35},
    }
    names := Map(users, func(u User) string {
        return u.Name
    })
    fmt.Println("User names:", names)
    // Output: [Alice Bob Charlie]

    // 带索引映射
    indexed := MapIdx(nums, func(i, n int) string {
        return fmt.Sprintf("[%d]=%d", i, n)
    })
    fmt.Println("Indexed:", indexed)
    // Output: [0=1 1=2 2=3 3=4 4=5]
}

33.7.3.2 泛型 Filter 函数

泛型 Filter——按条件过滤切片元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Filter 过滤出满足条件的元素
func Filter[T any](items []T, predicate func(T) bool) []T {
    result := []T{}
    for _, item := range items {
        if predicate(item) {
            result = append(result, item)
        }
    }
    return result
}

// FilterIdx 带索引的过滤
func FilterIdx[T any](items []T, predicate func(int, T) bool) []T {
    result := []T{}
    for i, item := range items {
        if predicate(i, item) {
            result = append(result, item)
        }
    }
    return result
}

// FilterNot 取反
func FilterNot[T any](items []T, predicate func(T) bool) []T {
    return Filter(items, func(item T) bool {
        return !predicate(item)
    })
}

// Partition 将切片分为两部分:满足条件的和不满足的
func Partition[T any](items []T, predicate func(T) bool) (yes []T, no []T) {
    yes = []T{}
    no = []T{}
    for _, item := range items {
        if predicate(item) {
            yes = append(yes, item)
        } else {
            no = append(no, item)
        }
    }
    return
}

func main() {
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    // 过滤偶数
    evens := Filter(nums, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println("Evens:", evens)
    // Output: [2 4 6 8 10]

    // 过滤奇数
    odds := FilterNot(nums, func(n int) bool {
        return n%2 == 0
    })
    fmt.Println("Odds:", odds)
    // Output: [1 3 5 7 9]

    // 带索引过滤:过滤索引为偶数的元素
    evenIdx := FilterIdx(nums, func(i, n int) bool {
        return i%2 == 0
    })
    fmt.Println("Even index elements:", evenIdx)
    // Output: [1 3 5 7 9]

    // 分区
    passed, failed := Partition(nums, func(n int) bool {
        return n >= 5
    })
    fmt.Println(">= 5:", passed, "< 5:", failed)
    // Output: >= 5: [5 6 7 8 9 10] < 5: [1 2 3 4]
}

33.7.3.3 泛型 Reduce 函数

泛型 Reduce——将切片元素归约为单个值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Reduce 将切片元素归约为一个值
func Reduce[T, U any](items []T, reducer func(U, T) U, initial U) U {
    result := initial
    for _, item := range items {
        result = reducer(result, item)
    }
    return result
}

// ReduceIdx 带索引的Reduce
func ReduceIdx[T, U any](items []T, reducer func(U, int, T) U, initial U) U {
    result := initial
    for i, item := range items {
        result = reducer(result, i, item)
    }
    return result
}

// Count 计数
func Count[T any](items []T, predicate func(T) bool) int {
    count := 0
    for _, item := range items {
        if predicate(item) {
            count++
        }
    }
    return count
}

// Sum 求和(需要泛型约束)
func Sum[T any](items []T, getValue func(T) int) int {
    return Reduce(items, func(sum int, item T) int {
        return sum + getValue(item)
    }, 0)
}

func main() {
    nums := []int{1, 2, 3, 4, 5}

    // 求和
    sum := Reduce(nums, func(acc, n int) int {
        return acc + n
    }, 0)
    fmt.Println("Sum:", sum)
    // Output: 15

    // 求积
    product := Reduce(nums, func(acc, n int) int {
        return acc * n
    }, 1)
    fmt.Println("Product:", product)
    // Output: 120

    // 找最大值
    max := Reduce(nums, func(acc, n int) int {
        if n > acc {
            return n
        }
        return acc
    }, 0)
    fmt.Println("Max:", max)
    // Output: 5

    // 连接字符串
    strs := []string{"Hello", " ", "World", "!"}
    concat := Reduce(strs, func(acc, s string) string {
        return acc + s
    }, "")
    fmt.Println("Concat:", concat)
    // Output: Hello World!

    // 复杂类型求和
    users := []User{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 35},
    }
    totalAge := Sum(users, func(u User) int {
        return u.Age
    })
    fmt.Println("Total age:", totalAge)
    // Output: 90

    // 带索引的Reduce
    indexedSum := ReduceIdx(nums, func(acc, i, n int) int {
        return acc + i*n
    }, 0)
    fmt.Println("Indexed sum:", indexedSum)
    // Output: 0*1 + 1*2 + 2*3 + 3*4 + 4*5 = 40
}

函数式编程的"链式调用"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 链式调用示例
func main() {
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

    // 链式:过滤偶数 -> 乘以2 -> 求和
    result := Reduce(
        Map(
            Filter(nums, func(n int) bool { return n%2 == 0 }),
            func(n int) int { return n * 2 },
        ),
        func(acc, n int) int { return acc + n },
        0,
    )
    fmt.Println("Result:", result)
    // Output: (2+4+6+8+10)*2 = 60
}

下一个小节预告:33.8 泛型性能——泛型到底有没有运行时开销!


33.8 泛型性能

泛型会不会有运行时性能开销?这是很多Go开发者关心的问题。

33.8.1 类型特化

类型特化——Go编译器会为每种具体类型生成专门代码,泛型函数在运行时实际上是"特化"后的代码,没有额外的运行时开销。

1
2
3
4
5
6
7
8
// 泛型函数
func Double[T int | float64](v T) T {
    return v * 2
}

// 编译后变成两个具体函数:
// func DoubleInt(v int) int { return v * 2 }
// func DoubleFloat64(v float64) float64 { return v * 2 }

类型特化的原理

graph TD
    A["源代码"] --> B["编译器处理泛型"]
    B --> C["为每种类型生成专门代码"]
    C --> D["int版本"]
    C --> E["float64版本"]
    C --> F["string版本"]
    D --> G["运行时代码"]
    E --> G
    F --> G
    
    style B fill:#FFD700

性能对比

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "testing"

// 泛型版本
func SumGeneric[T int | float64](values []T) T {
    var sum T
    for _, v := range values {
        sum += v
    }
    return sum
}

// 接口版本(传统的空接口方式)
func SumInterface(values []interface{}) interface{} {
    var sum int
    for _, v := range values {
        sum += v.(int)
    }
    return sum
}

// 测试
func BenchmarkSumGeneric(b *testing.B) {
    nums := make([]int, 1000)
    for i := range nums {
        nums[i] = i
    }
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        SumGeneric(nums)
    }
}

func main() {
    // 泛型版本:性能与普通函数几乎一样
    // 接口版本:有类型断言开销,性能较差
}

泛型 vs 接口的性能对比

实现方式性能类型安全代码量
泛型几乎零开销编译时检查
空接口+类型断言有运行时开销运行时检查

33.8.2 编译时代码生成

编译时代码生成——Go编译器在编译时生成特化代码,所以泛型函数在运行时没有额外的调度开销。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 泛型函数在编译时就"展开"了
// 类似于C++的模板特化

// 源代码
func Max[T int | float64](a, b T) T {
    if a > b {
        return a
    }
    return b
}

// 编译器生成的实际代码类似:
// func MaxInt(a, b int) int {
//     if a > b { return a }
//     return b
// }
// func MaxFloat64(a, b float64) float64 {
//     if a > b { return a }
//     return b
// }

编译时代码生成的"优点"

graph LR
    A["编译时代码生成"] --> B["无虚拟表查找"]
    A --> C["无类型断言开销"]
    A --> D["编译器优化"]
    A --> E["直接调用"]
    
    style A fill:#90EE90

33.8.3 与接口性能对比

与接口性能对比——泛型比使用空接口+反射的方式快很多。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import "testing"

// 泛型方式
func FindGeneric[T comparable](items []T, target T) int {
    for i, item := range items {
        if item == target {
            return i
        }
    }
    return -1
}

// 接口方式(反射)
func FindInterface(items interface{}, target interface{}) int {
    // 使用反射遍历...(省略具体实现)
    return -1
}

// Benchmarks
func BenchmarkFindGeneric(b *testing.B) {
    nums := make([]int, 1000)
    for i := range nums {
        nums[i] = i
    }
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        FindGeneric(nums, 500)
    }
}

性能结论

1
2
3
4
BenchmarkFindGeneric      性能:与普通函数几乎相同
BenchmarkFindInterface    性能:明显更慢(反射开销)

泛型性能 ≈ 普通函数性能 >> 接口+反射性能

什么时候泛型会有性能问题

1
2
3
4
5
6
7
// ⚠️ 泛型切片作为接口传递时会有装箱开销
func BadExample[T any](items []T) {
    // items是[]T,在某些情况下可能装箱
}

// ⚠️ 泛型方法作为接口值时有开销
var fn func(int) int = Double  // 可能有少量开销

下一个小节预告:33.9 泛型陷阱——使用泛型时容易犯的错误!


33.9 泛型陷阱

泛型虽然强大,但使用不当也会带来问题。这一节我们来看看常见的"陷阱"。

33.9.1 类型参数过多

类型参数过多——泛型函数的类型参数太多会让代码难以理解和维护。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ❌ 过多类型参数:难以理解
func ComplexFunction[
    A any,
    B comparable,
    C interface{ String() string },
    D int | float64,
    E any,
](a A, b B, c C, d D, e E) { }

// ✅ 建议:类型参数不超过3个
// 如果超过,考虑重构

类型参数过多的"问题"

graph LR
    A["类型参数过多"] --> B["代码可读性差"]
    A --> C["调用时需要显式指定"]
    A --> D["难以维护"]
    
    style A fill:#FFB6C1

重构建议

1
2
3
4
5
6
7
8
// 将相关类型参数组合成结构体
type Params[T any] struct {
    A T
    B comparable
    C interface{ String() string }
}

func SimplerFunction[T int | float64, P Params[T]](p P) { }

33.9.2 约束过于宽松

约束过于宽松——使用any约束会让代码失去类型安全。

1
2
3
4
5
6
7
8
9
// ❌ 约束过于宽松
func Double[T any](v T) T {
    return v * 2  // 编译错误!T是any,不知道如何相乘
}

// ✅ 约束要精确
func Double[T int | float64](v T) T {
    return v * 2  // works
}

约束过于宽松的"后果"

1
2
3
4
5
// ⚠️ any约束无法进行类型特定操作
func BadFunction[T any](v T) T {
    // 无法对T做任何操作!
    // 因为any不代表"任意支持操作的类型"
}

33.9.3 方法不支持泛型

方法不支持泛型——Go的方法不能有自己的类型参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// ❌ 错误:方法不能有独立的类型参数
type Container[T any] struct {
    items []T
}

func (c *Container[T]) BadMethod[U any](x U) { }  // 编译错误!

// ✅ 正确:使用接收器的类型参数
func (c *Container[T]) GoodMethod(f func(T) T) {
    for i, item := range c.items {
        c.items[i] = f(item)
    }
}

Go方法泛型的限制

1
2
3
4
5
// ⚠️ 方法不能这样定义:
func (t *Tree[T]) Search[U any](target U) *T  // 编译错误

// ✅ 方法应该使用接收器的类型参数
func (t *Tree[T]) Search(predicate func(T) bool) *T

下一个小节预告:33.10 泛型实现原理——Go编译器是如何处理泛型的!


33.10 泛型实现原理

了解泛型的实现原理能帮助你写出更好的泛型代码。

33.10.1 字典传递

字典传递(Dictionary Passing)——Go编译器为每种类型参数组合生成一个"类型字典",在调用时传递。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 泛型函数
func Map[T, U any](items []T, fn func(T) U) []U {
    result := make([]U, len(items))
    for i, item := range items {
        result[i] = fn(item)
    }
    return result
}

// 编译后类似:
// type MapDictionary struct {
//     // 包含T和U的约束信息
// }
// func MapGenerated[T, U any](items []T, dict *MapDictionary) []U

字典传递的"工作方式"

graph LR
    A["泛型函数调用"] --> B["编译器生成类型字典"]
    B --> C["字典传递给生成的函数"]
    C --> D["运行时使用字典信息"]
    
    style A fill:#87CEEB
    style D fill:#90EE90

33.10.2 GCShape

GCShape——Go运行时使用GCShape来跟踪不同类型的"形状",用于垃圾回收和类型信息。

1
2
3
4
5
6
// 不同类型的GCShape可能相同
type MyInt int
type AnotherInt int

// MyInt和AnotherInt可能有相同的GCShape
// 因为它们底层都是int

GCShape的作用

graph TD
    A["GCShape"] --> B["垃圾回收"]
    A --> C["类型信息"]
    A --> D["内存布局"]
    
    style A fill:#FFD700

33.10.3 类型字典

类型字典——编译器为每种类型实参组合生成的元数据字典。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 泛型函数
func Process[T any](v T) {
    fmt.Println(v)
}

// 为int生成的字典:
// ProcessIntDictionary = {
//     TypeInfo: ...,
//     Methods: [...],
//     ...
// }

33.10.4 方法字典

方法字典——当泛型类型的方法被调用时,编译器通过方法字典找到正确的方法实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
type Container[T any] struct {
    value T
}

func (c *Container[T]) Get() T {
    return c.value
}

// 每种T都会生成对应的方法字典
// ContainerInt_Get_MethodDict
// ContainerString_Get_MethodDict

下一个小节预告:33.11 泛型约束设计进阶——更高级的约束设计技巧!


33.11 泛型约束设计进阶

33.11.1 约束推导算法

约束推导算法——Go编译器使用复杂的算法来推导类型参数需要满足的约束。

1
2
3
4
5
6
7
// Go自动推导T必须有<操作
func Min[T any](a, b T) T {
    if a < b {  // Go看到<操作,推导出T必须是comparable
        return a
    }
    return b
}

约束推导的"规则"

操作推导的约束
<, >, <=, >=comparable
==, !=comparable
+, -, *, /需要明确声明(如int、float64)

33.11.2 类型集运算

类型集运算——约束本质上定义了类型的"集合",可以进行集合运算。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 并集
type AorB interface {
    int | string
}

// 交集
type IntAndSigned interface {
    int | int32
    ~int | ~int64
}
// IntAndSigned = int(只有int同时满足两个约束)

33.11.3 约束简化

约束简化——复杂的约束可以简化为更清晰的定义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 简化前
type ComplexConstraint interface {
    int | int8 | int16 | int32 | int64 |
    uint | uint8 | uint16 | uint32 | uint64 |
    float32 | float64
}

// 简化后
type Numeric interface {
    constraints.Integer | constraints.Float
}

下一个小节预告:33.12 泛型与反射——泛型和反射的关系!


33.12 泛型与反射

泛型和反射都能处理类型信息,但方式不同。

33.12.1 泛型类型反射

泛型类型反射——泛型类型实例化后的具体类型可以通过反射获取。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
    "fmt"
    "reflect"
)

type Container[T any] struct {
    items []T
}

func main() {
    // 泛型类型实例化
    intContainer := Container[int]{items: []int{1, 2, 3}}
    strContainer := Container[string]{items: []string{"a", "b", "c"}}

    // 使用反射获取类型信息
    t1 := reflect.TypeOf(intContainer)
    fmt.Println("intContainer type:", t1)
    // Output: Container[int]

    t2 := reflect.TypeOf(strContainer)
    fmt.Println("strContainer type:", t2)
    // Output: Container[string]
}

33.12.2 类型参数获取

类型参数获取——通过反射获取泛型类型的类型参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
    "fmt"
    "reflect"
)

type Container[T any] struct {
    items []T
}

func main() {
    // 获取泛型类型的类型参数
    containerType := reflect.TypeOf(Container[int]{})
    
    // 类型参数
    tParam := containerType.TypeParams()
    fmt.Println("Type parameters:", tParam)
    // Output: [T any]

    // 获取具体的类型实参
    if tParam.Len() > 0 {
        fmt.Println("T is:", tParam.At(0))
        // Output: int
    }
}

33.12.3 泛型与 any

泛型与 any——泛型和any(空接口)有本质区别。

1
2
3
4
5
6
7
8
// any = interface{},运行时丢失类型信息
var v any = 42
// v现在是interface{},失去了int的类型信息

// 泛型在编译时就确定了类型
func Double[T int](v T) T {
    return v * 2  // 编译时就确定是int的乘法
}

泛型与 any 的对比

特性泛型any
类型检查编译时运行时
性能零开销有装箱开销
类型安全
适用场景静态多态动态多态

下一个小节预告:33.13 泛型最佳实践——如何写出好的泛型代码!


33.13 泛型最佳实践

33.13.1 何时使用泛型

何时使用泛型——泛型不是万能药,要在合适的场景使用。

适合使用泛型的场景

graph LR
    A["适合泛型的场景"] --> B["数据结构:栈、队列、链表、树"]
    A --> C["通用算法:排序、查找、过滤"]
    A --> D["类型安全的容器"]
    A --> E["减少重复代码"]
    
    style A fill:#90EE90

不适合使用泛型的场景

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// ❌ 为了"赶时髦"而使用泛型
func BadExample[T any](x T) T {
    return x  // 完全没有必要
}

// ❌ 只有一个类型参数的特殊情况
func SingleCase[T int](x T) T {  // 直接用int就行
    return x * 2
}

// ❌ 简单的类型转换
func ToString[T any](v T) string {
    return fmt.Sprintf("%v", v)  // 用fmt.Sprintf就行
}

何时用泛型的判断标准

问题用泛型?原因
多个类型需要相同逻辑减少代码重复
需要类型安全编译时检查
只是简单包装过度设计
性能敏感的热路径⚠️需要测试验证

33.13.2 泛型与接口选择

泛型与接口选择——什么时候用泛型,什么时候用接口?

1
2
3
4
5
6
7
8
9
// 用泛型:需要类型安全,且类型数量有限
func Double[T int | float64](v T) T {
    return v * 2
}

// 用接口:需要运行时灵活性
type Doubler interface {
    Double() interface{}
}

选择指南

graph TD
    A["选择泛型还是接口?"] --> B{"类型在编译时已知?"}
    B -->|是| C["泛型"]
    B -->|否| D["接口"]
    
    style C fill:#90EE90
    style D fill:#87CEEB

33.13.3 泛型代码可读性

泛型代码可读性——让泛型代码保持清晰易懂。

1
2
3
4
5
6
7
// ✅ 好的命名和注释
// Sum 求和函数,支持所有数值类型
func Sum[T int | float64 | int64](values []T) T

// ❌ 过于简洁的命名
// F 泛型函数(不知所云)
func F[T any](v T) T

可读性建议

1
2
3
4
5
6
7
8
9
// ✅ 类型参数要有实际意义
type Pair[K, V any]     // Key-Value
type Stack[T any]       // T是元素类型
type TreeNode[T any]    // T是节点值类型

// ✅ 复杂约束单独定义
type Numeric interface {
    int | int32 | int64 | float32 | float64
}

下一个小节预告:33.14 泛型与接口的对比选择——最终对决!


33.14 泛型与接口的对比选择

33.14.1 性能对比

性能对比——泛型和接口在性能上有显著差异。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 泛型版本
func SumGeneric[T int | float64](values []T) T {
    var sum T
    for _, v := range values {
        sum += v
    }
    return sum
}

// 接口版本(需要类型断言)
func SumInterface(values []any) any {
    var sum int
    for _, v := range values {
        sum += v.(int)
    }
    return sum
}

性能测试结果

方式性能说明
泛型几乎零开销编译时特化
空接口+断言有开销装箱+断言
反射很大开销运行时反射

33.14.2 代码生成对比

代码生成对比——泛型和代码生成的对比。

1
2
3
4
5
6
// 泛型:编译器自动生成
func Double[T int | float64](v T) T

// 代码生成:手动编写每种类型
func DoubleInt(v int) int
func DoubleFloat64(v float64) float64
方式代码量维护性性能
泛型
代码生成

33.14.3 使用场景选择

使用场景选择——根据场景选择合适的方式。

graph TD
    A["选择指南"] --> B["需要类型安全 + 编译时确定 → 泛型"]
    A --> C["需要运行时灵活性 → 接口"]
    A --> D["需要代码生成 → 生成工具"]
    
    style B fill:#90EE90
    style C fill:#87CEEB
    style D fill:#FFD700

选择决策树

问题选择
类型在编译时确定?✅ 泛型
需要强制类型约束?✅ 泛型
类型运行时才确定?✅ 接口
需要动态添加方法?✅ 接口

33.14.4 迁移建议

迁移建议——从空接口迁移到泛型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 旧代码(空接口)
type Container struct {
    items []interface{}
}

func (c *Container) Add(item interface{}) {
    c.items = append(c.items, item)
}

func (c *Container) Get(i int) interface{} {
    return c.items[i]
}

// 新代码(泛型)
type Container[T any] struct {
    items []T
}

func (c *Container[T]) Add(item T) {
    c.items = append(c.items, item)
}

func (c *Container[T]) Get(i int) T {
    return c.items[i]
}

迁移步骤

1
2
3
4
5
6
7
8
9
# 1. 识别空接口的使用
grep -r "interface{}" *.go

# 2. 确定类型参数
# 分析代码中实际使用的类型

# 3. 创建泛型版本
# 4. 替换调用点
# 5. 测试验证

第33章总结

graph LR
    A["第33章:泛型"] --> B["类型参数"]
    A --> C["类型约束"]
    A --> D["泛型类型"]
    A --> E["泛型函数"]
    A --> F["泛型算法"]
    A --> G["性能与陷阱"]
    
    style A fill:#FFD700

恭喜你完成了第33章的学习!泛型是Go 1.18引入的强大特性,掌握它能让你写出更加优雅、高效和类型安全的代码。


第33章 泛型 内容已完成

最后修改 March 20, 2026: 新增Go基础部分 (8cce995)