go 泛型

By kcersing , 10 五月, 2026

go1.18之后引入
核心概念
泛型主要通过 类型参数(Type Parameters) 和 类型约束(Type Constraints) 来实现。
类型参数 (Type Parameters)
类型参数是一个占位符,代表一个未知的类型。在定义函数或类型时,你可以声明一个或多个类型参数。
语法:使用方括号 [] 来声明类型参数,通常放在函数名或类型名之后
如 
// T 就是一个类型参数
func Show[T any](s []T){}

类型约束 (Type Constraints)
类型约束定义了类型参数可以接受哪些具体的类型。它是一个接口类型,规定了类型参数必须满足的条件(比如必须支持某些操作或拥有某些方法)。
Go 内置了两个常用的约束:
1.any: 它是 interface{} 的别名,表示任何类型都可以作为类型参数。这是最宽松的约束。
2.comparable: 表示该类型支持使用 == 和 != 进行比较。所有基础类型(如 int, string, bool)都满足这个约束,但像 slice、map 和 func 则不满足。
如何使用泛型
泛型函数 (Generic Functions)
下面是一个简单的泛型函数示例,它可以打印任何类型的切片(slice)。

import "fmt"

// Show 是一个泛型函数
// [T any] 定义了一个名为 T 的类型参数,它的约束是 any(任何类型)
func Show[T any](s []T) {
   for _, v := range s {
       fmt.Printf("%v ", v)
   }
   fmt.Println()
}

func main() {
   intShow := []int{1, 2, 3}
   stringShow := []string{"hello", "world"}

   // 调用泛型函数时,通常不需要显式指定类型
   // Go 编译器会自动推断出 T 是 int
   Show(intShow) // 输出: 1 2 3

   // 编译器会自动推断出 T 是 string
   Show(stringShow) // 输出: hello world
}

自定义约束
你也可以通过定义一个接口来创建自己的约束。例如,我们想写一个泛型函数来求两个数的和,那么这两个数必须支持 + 操作。


// Number 是一个自定义约束,它约束了类型参数必须是 int 或 float64
type Number interface {
   int | float64 // 使用 | 来联合多个类型
}
// Add 是一个泛型函数,它只能接受满足 Number 约束的类型
func Add[T Number](a, b T) T {
   return a + b
}
func main() {
   fmt.Println(Add(1, 2))          // 输出: 3
   fmt.Println(Add(1.5, 2.5))      // 输出: 4.0
   // 下面这行会编译错误,因为 string 不满足 Number 约束
   // fmt.Println(Add("a", "b"))
}


泛型类型 (Generic Types)
除了函数,你还可以定义泛型的数据结构,比如一个栈(Stack)或链表。
下面是一个可以存放任何类型数据的泛型 Stack 的例子:


// Stack 是一个泛型类型
// [T any] 表示这个 Stack 可以存放任何类型的数据
type Stack[T any] struct {
   items []T
}
// Push 方法向栈中添加一个元素
func (s *Stack[T]) Push(item T) {
   s.items = append(s.items, item)
}
// Pop 方法从栈中弹出一个元素
func (s *Stack[T]) Pop() (T, bool) {
   if len(s.items) == 0 {
       var zero T // 声明一个 T 类型的零值
       return zero, false
   }
   item := s.items[len(s.items)-1]
   s.items = s.items[:len(s.items)-1]
   return item, true
}
func main() {
   // 创建一个存放 int 的栈
   intStack := &Stack[int]{}
   intStack.Push(10)
   intStack.Push(20)
   val, _ := intStack.Pop()
   fmt.Println(val) // 输出: 20
   // 创建一个存放 string 的栈
   stringStack := &Stack[string]{}
   stringStack.Push("A")
   stringStack.Push("B")
   strVal, _ := stringStack.Pop()
   fmt.Println(strVal) // 输出: B
}

Go 泛型的优势:
1.代码复用:为多种类型编写通用的、一次性的代码。
2.类型安全:在编译时就能捕获类型错误,而不是在运行时。
3.代码更清晰:不再需要 interface{} 和类型断言,逻辑更直观。