Skip to content

泛型(Generics)

本章讲解Go泛型:泛型历史、类型参数与约束、泛型函数与数据结构、适用场景与误区。重点:泛型在Go 1.18引入,用于减少类型断言的运行时开销;理解any约束和自定义约束的用法。

前置知识:第6章 接口 学习目标:掌握泛型语法、理解约束的作用、避免泛型滥用


  • Go 1.0 - 1.17:无泛型,使用interface和类型断言
  • Go 1.18(2022年3月):正式引入泛型
// 使用interface{}存储任意类型
type Stack struct {
items []interface{}
}
func (s *Stack) Push(item interface{}) {
s.items = append(s.items, item)
}
func (s *Stack) Pop() interface{} {
if len(s.items) == 0 {
return nil
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item
}
// 使用需要类型断言
stack := &Stack{}
stack.Push(1)
stack.Push("hello")
item := stack.Pop().(string) // 类型断言,可能panic
// 泛型Stack
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 {
if len(s.items) == 0 {
var zero T
return zero
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item
}
// 使用时指定类型
stack := &Stack[int]{}
stack.Push(1)
// stack.Push("hello") // 编译错误:必须是int

本节目的:掌握泛型类型参数语法、any约束和自定义约束

// 类型参数用方括号
func printSlice[T any](s []T) {
for _, v := range s {
fmt.Print(v, " ")
}
fmt.Println()
}
// 完整语法
func comparison[T comparable](a, b T) bool {
return a == b
}

any是空接口的别名,等同于interface{}:

// 这两个等价
func f1[T any](x T) {}
func f2[T interface{}](x T) {}

comparable约束表示类型可以比较(可以用==和!=):

// 泛型函数要求类型可比较
func contains[T comparable](s []T, target T) bool {
for _, v := range s {
if v == target {
return true
}
}
return false
}
func main() {
fmt.Println(contains([]int{1, 2, 3}, 2)) // true
fmt.Println(contains([]string{"a", "b"}, "c")) // false
}
// 定义约束
type Number interface {
int | int32 | int64 | float64 | float32
}
func sum[T Number](values []T) T {
var total T
for _, v := range values {
total += v
}
return total
}
func main() {
fmt.Println(sum([]int{1, 2, 3})) // 6
fmt.Println(sum([]float64{1.5, 2.5})) // 4.0
}
// 允许的类型联合
type StringOrInt interface {
string | int
}
func convert[T StringOrInt](v T) string {
return fmt.Sprintf("%v", v)
}
func main() {
fmt.Println(convert("hello")) // hello
fmt.Println(convert(123)) // 123
}

本节目的:掌握泛型函数编写、泛型数据结构的实现

// 交换两个值
func swap[T any](a, b T) (T, T) {
return b, a
}
// 找最大值
func max[T comparable](a, b T) T {
if a >= b {
return a
}
return b
}
// Map函数
func mapSlice[T, 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
}
func main() {
numbers := []int{1, 2, 3}
strings := mapSlice(numbers, func(n int) string {
return fmt.Sprintf("num_%d", n)
})
fmt.Println(strings) // [num_1 num_2 num_3]
}
// 泛型Pair
type Pair[K, V any] struct {
Key K
Value V
}
// 泛型链表
type List[T any] struct {
value T
next *List[T]
}
// 泛型Tree
type Tree[T any] struct {
value T
left *Tree[T]
right *Tree[T]
}
type Container[T any] struct {
items []T
}
// 方法可以继续使用泛型
func (c *Container[T]) Get(index int) T {
return c.items[index]
}
func (c *Container[T]) Size() int {
return len(c.items)
}

本节目的:理解泛型的适用场景、避免常见误区

1. 数据结构

// 通用数据结构
type Stack[T any] struct {
items []T
}
type Queue[T any] struct {
items []T
}
type HashMap[K comparable, V any] struct {
// ...
}

2. 算法实现

// 通用排序
func sort[T any](s []T, less func(a, b T) bool) []T {
// 排序逻辑
copy(s, sortUtil(s, less))
return s
}
// 使用
data := []int{3, 1, 2}
sort(data, func(a, b int) bool {
return a < b
})

3. 工具函数

// 管道式处理
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
}

误区1:泛型影响运行时性能

实际上,泛型在编译时展开,无运行时开销:

// 泛型
func identity[T any](v T) T { return v }
// 编译后类似:
func identity_int(v int) int { return v }
func identity_string(v string) string { return v }

误区2:所有场景都应用泛型

简单场景用具体类型更清晰:

// 过度泛型
func process[T any](data []T) []T { ... }
// 更清晰
func processInts(data []int) []int { ... }

误区3:泛型可以替代interface

泛型用于类型安全的数据结构和算法,interface用于运行时多态。

// 泛型:编译时知道类型
type Cache[T any] struct {
data map[string]T
}
// interface{}:运行时才知道类型
type AnyCache struct {
data map[string]interface{}
}
// 选择泛型:类型参数影响函数签名
func first[T any](s []T) T {
return s[0]
}
// 选择interface:需要多种类型实现同一行为
type Stringer interface {
String() string
}
func printAll[T Stringer](items []T) {
for _, item := range items {
fmt.Println(item.String())
}
}

  1. 泛型用于数据结构:Stack、Queue、LinkedList等数据结构用泛型,避免类型断言
  2. any约束最宽松:[T any]允许任何类型,用于不依赖类型行为的场景 3 comparable约束比较:[T comparable]用于需要==/!=操作的类型参数
  3. 不要滥用泛型:如果只有两三个类型需要处理,用具体类型更清晰
  4. 约束让代码更安全:用自定义约束限制类型参数的能力,如[T Number]限制为数字类型

Go 1.17-Go 1.18+
interface{}存储任意类型any(interface别名)
类型断言泛型函数
无[T any]类型参数
无comparable约束

泛型使用原则:

  1. 泛型用于数据结构和通用算法
  2. 约束让类型参数更精确
  3. 泛型无运行时开销
  4. 不要过度使用泛型

  1. 实现泛型Stack数据结构
  2. 实现泛型Map函数,处理切片并返回新切片
  3. 定义约束实现整数求和函数
  4. 实现泛型链表
  5. 比较interface和泛型的使用场景