导航菜单

泛型(Generics)是 Go 1.18 引入的最重要特性之一,允许编写与类型无关的代码,减少重复,提升类型安全性。本章将从设计动机出发,逐步讲解泛型语法、类型约束、泛型数据结构以及标准库中的泛型支持。

设计动机

在泛型出现之前,Go 语言处理”多种类型共享同一逻辑”的常见手段是:

  • 空接口 interface{}:丢失类型信息,运行时需要类型断言
  • 代码复制:为 intstringfloat64 分别写一份几乎相同的函数
  • 代码生成:通过 go generate 生成重复代码
// 没有泛型之前:三种类型的重复函数
func SumInts(s []int) int {
    total := 0
    for _, v := range s { total += v }
    return total
}
func SumFloats(s []float64) float64 {
    total := 0.0
    for _, v := range s { total += v }
    return total
}

// 有了泛型之后:一个函数搞定
func Sum[T int | float64](s []T) T {
    var total T
    for _, v := range s { total += v }
    return total
}

泛型函数语法

泛型函数在函数名后用方括号声明类型参数,格式为 func Name[T Constraint](params) returnType

泛型函数声明

func FuncName[T Constraint](x T) TT 是类型参数,Constraint 是类型约束(接口)。any 等价于 interface{},表示任意类型。

// 基本泛型函数
func Identity[T any](x T) T {
    return x
}

// 多个类型参数
func MapKeys[K comparable, V any](m map[K]V) []K {
    keys := make([]K, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    return keys
}

// 调用时可以显式指定类型,也可以由编译器推断
a := Identity(42)              // 推断 T = int
b := Identity[string]("hello") // 显式指定 T = string
keys := MapKeys(map[string]int{"a": 1, "b": 2})

Map / Filter / Reduce 泛型函数示例

利用泛型,我们可以实现经典的函数式编程组合:

// Map:将切片中每个元素转换为另一种类型
func Map[T any, U any](s []T, f func(T) U) []U {
    result := make([]U, len(s))
    for i, v := range s {
        result[i] = f(v)
    }
    return result
}

// Filter:根据谓词过滤切片
func Filter[T any](s []T, predicate func(T) bool) []T {
    result := make([]T, 0)
    for _, v := range s {
        if predicate(v) {
            result = append(result, v)
        }
    }
    return result
}

// Reduce:将切片归约为单个值
func Reduce[T any, U any](s []T, initial U, f func(U, T) U) U {
    acc := initial
    for _, v := range s {
        acc = f(acc, v)
    }
    return acc
}
// 使用示例
nums := []int{1, 2, 3, 4, 5}

doubled := Map(nums, func(n int) int { return n * 2 })
// doubled = [2, 4, 6, 8, 10]

evens := Filter(nums, func(n int) bool { return n%2 == 0 })
// evens = [2, 4]

sum := Reduce(nums, 0, func(acc, n int) int { return acc + n })
// sum = 15

// 跨类型 Map
strs := Map(nums, func(n int) string { return fmt.Sprintf("#%d", n) })
// strs = ["#1", "#2", "#3", "#4", "#5"]

泛型结构体 — Stack[T]

泛型不仅适用于函数,同样适用于结构体和方法:

// 泛型结构体定义
type Stack[T any] struct {
    items []T
}

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

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

func (s *Stack[T]) Peek() (T, bool) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    return s.items[len(s.items)-1], true
}

func (s *Stack[T]) Len() int {
    return len(s.items)
}

func (s *Stack[T]) IsEmpty() bool {
    return len(s.items) == 0
}
// 使用示例
intStack := &Stack[int]{}
intStack.Push(10)
intStack.Push(20)
val, _ := intStack.Pop() // val = 20

strStack := &Stack[string]{}
strStack.Push("hello")
strStack.Push("world")
top, _ := strStack.Peek() // top = "world"

类型约束

类型约束(Type Constraint)是泛型的核心机制,通过接口定义允许的类型集合。

any

anyinterface{} 的别名,表示不施加任何约束

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

comparable

comparable 是预定义约束,表示支持 ==!= 的所有类型(包括基本类型、字符串、数组、结构体,不包括切片、map、函数):

func Contains[T comparable](s []T, target T) bool {
    for _, v := range s {
        if v == target {
            return true
        }
    }
    return false
}

自定义约束(联合类型)

// 联合类型约束
type Number interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64
}

func Add[T Number](a, b T) T {
    return a + b
}

近似符号 ~

近似类型约束(~)

~T 表示底层类型(underlying type)为 T 的所有类型。例如 ~int 匹配 inttype MyInt int 等自定义类型。

type MyInt int
type YourInt int

// ~int 匹配 MyInt、YourInt 和 int
//  int  只匹配 int 本身
type SignedInteger interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

func Sum[T SignedInteger](s []T) T {
    var total T
    for _, v := range s { total += v }
    return total
}

// MyInt 和 YourInt 都可以调用 Sum
nums := []MyInt{1, 2, 3}
result := Sum(nums) // ✅ 正常工作

泛型的局限性

限制说明
不支持类型特化不能为不同类型提供不同的实现(如泛型版的 fmt.Stringer
不支持方法上的类型参数类型参数只能在函数级别声明,不能在方法级别额外声明
不支持 ==< 的泛型运算comparable 外,无法在泛型代码中使用比较/排序运算符
不能实例化泛型切片的元素类型make([]T, 0) 可以,但无法 new(T) 后设置字段
不支持可变参数泛型func Foo[T any](args ...T) 有限制场景
// ❌ 不支持特化 — 不能针对 string 提供不同实现
// func Max[string](a, b string) string { ... }

// ❌ 不支持方法级类型参数
// func (s *Stack[T]) Map[U any](f func(T) U) []U { ... }

// ✅ 变通方案:独立泛型函数
func MapStack[T any, U any](s *Stack[T], f func(T) U) []U {
    result := make([]U, s.Len())
    for i := 0; i < s.Len(); i++ {
        // 需要暴露内部访问方式
    }
    return result
}

slices 包和 maps 包

Go 1.21 引入了 slicesmaps 泛型工具包,提供常用的泛型操作:

slices 包

import "slices"

nums := []int{3, 1, 4, 1, 5, 9, 2, 6}

slices.Sort(nums)                    // 排序:[1, 1, 2, 3, 4, 5, 6, 9]
idx := slices.Index(nums, 4)         // 查找:返回 3
contains := slices.Contains(nums, 5) // 包含检查:true
reversed := slices.Reverse(nums)     // 原地反转

// 泛型排序(需要 cmp.Compare 或自定义比较函数)
names := []string{"Charlie", "Alice", "Bob"}
slices.Sort(names) // ["Alice", "Bob", "Charlie"]

// Clone 和 Concat
clone := slices.Clone(nums)
merged := slices.Concat([]int{1, 2}, []int{3, 4}) // [1, 2, 3, 4]

maps 包

import "maps"

m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"b": 3, "c": 4}

// 合并(相同 key,m2 的值覆盖 m1)
merged := maps.Clone(m1)
maps.Copy(merged, m2) // {"a": 1, "b": 3, "c": 4}

// 相等判断
equal := maps.Equal(map[string]int{"a": 1}, map[string]int{"a": 1}) // true

// 提取所有 key 和 value
keys := slices.Collect(maps.Keys(m1))  // ["a", "b"]
values := slices.Collect(maps.Values(m1)) // [1, 2]

最佳实践

  1. 优先使用 anycomparable:简单场景不需要自定义约束
  2. 约束尽量宽松:使用 ~ 近似类型让自定义类型也能受益
  3. 泛型函数 > 泛型方法:保持 API 简洁
  4. 不要为了泛型而泛型:只有 3 个以上类型复用时才考虑泛型
  5. 使用标准库泛型包:优先使用 slicesmaps 等标准库
  6. 保持类型参数名简短:单字母 TKV 是惯用命名
// ✅ 好的实践 — 逻辑真正跨类型复用
func Max[T cmp.Ordered](a, b T) T {
    if a > b { return a }
    return b
}

// ❌ 过度使用 — 只有 int 会用,不需要泛型
func ProcessInts[T any](nums []T) { ... } // 实际只处理 int

搜索