← 返回首页

泛型(Generics)

什么是泛型?

泛型是 Go 1.18 引入的重要特性,允许编写可以处理多种类型的代码,而不需要为每种类型重复编写相同的逻辑。泛型提高了代码的复用性和类型安全性。

泛型函数

package main

import "fmt"

// 泛型函数:使用类型参数
func Min[T cmp.Ordered](x, y T) T {
    if x < y {
        return x
    }
    return y
}

func main() {
    // 使用整数
    fmt.Println(Min(5, 3))  // 3

    // 使用浮点数
    fmt.Println(Min(3.14, 2.71))  // 2.71

    // 使用字符串
    fmt.Println(Min("apple", "banana"))  // apple
}

类型约束

package main

import "fmt"

// 自定义类型约束
type Number interface {
    int | int64 | float64
}

func Sum[T Number](numbers []T) T {
    var total T
    for _, num := range numbers {
        total += num
    }
    return total
}

func main() {
    ints := []int{1, 2, 3, 4, 5}
    floats := []float64{1.1, 2.2, 3.3}

    fmt.Println("整数和:", Sum(ints))
    fmt.Println("浮点数和:", Sum(floats))
}

泛型类型

package main

import "fmt"

// 泛型结构体
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 {
        var zero T
        return zero, false
    }
    index := len(s.items) - 1
    item := s.items[index]
    s.items = s.items[:index]
    return item, true
}

func (s *Stack[T]) IsEmpty() bool {
    return len(s.items) == 0
}

func main() {
    // 整数栈
    intStack := Stack[int]{}
    intStack.Push(1)
    intStack.Push(2)
    intStack.Push(3)

    for !intStack.IsEmpty() {
        val, _ := intStack.Pop()
        fmt.Println("弹出:", val)
    }

    // 字符串栈
    strStack := Stack[string]{}
    strStack.Push("Hello")
    strStack.Push("World")

    for !strStack.IsEmpty() {
        val, _ := strStack.Pop()
        fmt.Println("弹出:", val)
    }
}

泛型接口

package main

import "fmt"

// 泛型接口
type Container[T any] interface {
    Add(item T)
    Get(index int) T
    Size() int
}

type List[T any] struct {
    items []T
}

func (l *List[T]) Add(item T) {
    l.items = append(l.items, item)
}

func (l *List[T]) Get(index int) T {
    return l.items[index]
}

func (l *List[T]) Size() int {
    return len(l.items)
}

func PrintContainer[T any](c Container[T]) {
    for i := 0; i < c.Size(); i++ {
        fmt.Println(c.Get(i))
    }
}

func main() {
    list := List[int]{}
    list.Add(10)
    list.Add(20)
    list.Add(30)

    PrintContainer(&list)
}

泛型方法

package main

import "fmt"

type Processor struct {
    name string
}

// 泛型方法
func (p Processor) Process[T any](items []T, fn func(T) T) []T {
    result := make([]T, len(items))
    for i, item := range items {
        result[i] = fn(item)
    }
    return result
}

func main() {
    processor := Processor{name: "MyProcessor"}

    // 处理整数
    numbers := []int{1, 2, 3}
    doubled := processor.Process(numbers, func(n int) int {
        return n * 2
    })
    fmt.Println("加倍:", doubled)

    // 处理字符串
    words := []string{"hello", "world"}
    upper := processor.Process(words, func(s string) string {
        return s + "!"
    })
    fmt.Println("添加感叹号:", upper)
}

类型推断

package main

import "fmt"

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

func main() {
    // 类型推断:编译器自动推断类型
    Print(42)      // T 推断为 int
    Print("hello")  // T 推断为 string
    Print(3.14)     // T 推断为 float64

    // 显式指定类型
    Print[int](100)
}

多个类型参数

package main

import "fmt"

// 两个类型参数
func Pair[K, V any](key K, value V) map[K]V {
    return map[K]V{key: value}
}

// 三个类型参数
func Transform[T, U, V any](t T, u U, fn func(T, U) V) V {
    return fn(t, u)
}

func main() {
    // 使用 Pair
    m := Pair("name", "Go")
    fmt.Println(m)

    // 使用 Transform
    result := Transform(10, 5, func(a, b int) int {
        return a + b
    })
    fmt.Println("结果:", result)
}

使用 ~ 进行类型约束

package main

import "fmt"

// ~ 表示底层类型
type Ordered interface {
    ~int | ~float64 | ~string
}

type MyInt int
type MyFloat float64

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

func main() {
    // 支持自定义类型
    var x, y MyInt = 10, 20
    fmt.Println(Max(x, y))

    var f1, f2 MyFloat = 3.14, 2.71
    fmt.Println(Max(f1, f2))
}

泛型最佳实践

何时使用泛型

  • 当需要为多种类型实现相同逻辑时
  • 当类型安全比代码简洁更重要时
  • 当需要构建通用数据结构时(如栈、队列、树等)

何时不使用泛型

  • 当只需要处理一种特定类型时
  • 当使用接口更简单时
  • 当泛型会让代码更难理解时

实际应用示例:泛型缓存

package main

import (
    "fmt"
    "sync"
    "time"
)

type CacheItem[T any] struct {
    value      T
    expiration time.Time
}

type Cache[K comparable, V any] struct {
    items map[K]CacheItem[V]
    mu    sync.RWMutex
}

func NewCache[K comparable, V any() *Cache[K, V] {
    return &Cache[K, V]{
        items: make(map[K]CacheItem[V]),
    }
}

func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) {
    c.mu.Lock()
    defer c.mu.Unlock()

    c.items[key] = CacheItem[V]{
        value:      value,
        expiration: time.Now().Add(ttl),
    }
}

func (c *Cache[K, V]) Get(key K) (V, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()

    item, exists := c.items[key]
    if !exists || time.Now().After(item.expiration) {
        var zero V
        return zero, false
    }
    return item.value, true
}

func main() {
    // 字符串缓存
    stringCache := NewCache[string, string]()
    stringCache.Set("key1", "value1", 10*time.Second)
    val, ok := stringCache.Get("key1")
    fmt.Println("字符串缓存:", val, ok)

    // 整数缓存
    intCache := NewCache[string, int]()
    intCache.Set("counter", 42, 10*time.Second)
    count, ok := intCache.Get("counter")
    fmt.Println("整数缓存:", count, ok)
}