泛型(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)
}