泛型(Go 1.18+)
基本语法
// 泛型函数:类型参数在函数名后用方括号声明
func Max[T int | float64 | string](a, b T) T {
if a > b {
return a
}
return b
}
// 调用
Max(3, 5) // 类型推断:T=int
Max(3.14, 2.71) // 类型推断:T=float64
Max[int](3, 5) // 显式指定
Max("hello", "world") // T=string
类型约束
内置约束 comparable
// comparable:支持 == 和 != 的类型
func Contains[T comparable](slice []T, target T) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
Contains([]int{1, 2, 3}, 2) // true
Contains([]string{"a", "b"}, "c") // false
自定义约束
// 联合类型约束
type Number interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
func Sum[T Number](numbers []T) T {
var sum T
for _, n := range numbers {
sum += n
}
return sum
}
Sum([]int{1, 2, 3}) // 6
Sum([]float64{1.1, 2.2}) // 3.3
~ 符号(底层类型约束)
// ~ 表示包含底层类型为 T 的所有自定义类型
type Number interface {
~int | ~int64 | ~float32 | ~float64
}
type Celsius float64 // 底层类型是 float64
type Score int // 底层类型是 int
func Double[T Number](v T) T {
return v * 2
}
Double(Celsius(36.5)) // ✓ 有 ~ 才能用
Double(Score(95)) // ✓ 有 ~ 才能用
constraints 包(golang.org/x/exp/constraints)
import "golang.org/x/exp/constraints"
// Ordered:所有支持 < > <= >= 的类型
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
// Signed / Unsigned / Integer / Float / Ordered
// 这些约束在 Go 1.21+ 已移入标准库 cmp 包
泛型数据结构
泛型 Stack
type Stack[T any] struct {
items []T
}
func NewStack[T any]() *Stack[T] {
return &Stack[T]{}
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
var zero T
if len(s.items) == 0 {
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
func (s *Stack[T]) Peek() (T, bool) {
var zero T
if len(s.items) == 0 {
return zero, false
}
return s.items[len(s.items)-1], true
}
func (s *Stack[T]) Len() int {
return len(s.items)
}
// 使用
intStack := NewStack[int]()
intStack.Push(1)
intStack.Push(2)
v, _ := intStack.Pop() // 2
strStack := NewStack[string]()
strStack.Push("hello")
泛型 Set
type Set[T comparable] struct {
items map[T]struct{}
}
func NewSet[T comparable](items ...T) *Set[T] {
s := &Set[T]{items: make(map[T]struct{})}
for _, item := range items {
s.items[item] = struct{}{}
}
return s
}
func (s *Set[T]) Add(item T) {
s.items[item] = struct{}{}
}
func (s *Set[T]) Remove(item T) {
delete(s.items, item)
}
func (s *Set[T]) Contains(item T) bool {
_, ok := s.items[item]
return ok
}
func (s *Set[T]) Union(other *Set[T]) *Set[T] {
result := NewSet[T]()
for k := range s.items {
result.Add(k)
}
for k := range other.items {
result.Add(k)
}
return result
}
func (s *Set[T]) Intersect(other *Set[T]) *Set[T] {
result := NewSet[T]()
for k := range s.items {
if other.Contains(k) {
result.Add(k)
}
}
return result
}
func (s *Set[T]) ToSlice() []T {
result := make([]T, 0, len(s.items))
for k := range s.items {
result = append(result, k)
}
return result
}
泛型 Map 工具函数
// Map:对 slice 每个元素应用函数
func Map[T, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
// Filter:过滤 slice
func Filter[T any](slice []T, fn func(T) bool) []T {
result := make([]T, 0)
for _, v := range slice {
if fn(v) {
result = append(result, v)
}
}
return result
}
// Reduce:归约
func Reduce[T, U any](slice []T, initial U, fn func(U, T) U) U {
result := initial
for _, v := range slice {
result = fn(result, v)
}
return result
}
// 使用
nums := []int{1, 2, 3, 4, 5}
// 每个元素 *2
doubled := Map(nums, func(n int) int { return n * 2 })
// [2, 4, 6, 8, 10]
// 过滤偶数
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
// [2, 4]
// 求和
sum := Reduce(nums, 0, func(acc, n int) int { return acc + n })
// 15
// 类型转换
strs := Map(nums, strconv.Itoa)
// ["1", "2", "3", "4", "5"]
泛型 Pair / Tuple
type Pair[A, B any] struct {
First A
Second B
}
func NewPair[A, B any](a A, b B) Pair[A, B] {
return Pair[A, B]{First: a, Second: b}
}
// 使用
p := NewPair("小徐", 28)
// p.First = "小徐"
// p.Second = 28
泛型 Cache
type Cache[K comparable, V any] struct {
mu sync.RWMutex
items map[K]V
ttl time.Duration
expires map[K]time.Time
}
func NewCache[K comparable, V any](ttl time.Duration) *Cache[K, V] {
return &Cache[K, V]{
items: make(map[K]V),
expires: make(map[K]time.Time),
ttl: ttl,
}
}
func (c *Cache[K, V]) Set(key K, value V) {
c.mu.Lock()
defer c.mu.Unlock()
c.items[key] = value
c.expires[key] = time.Now().Add(c.ttl)
}
func (c *Cache[K, V]) Get(key K) (V, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
var zero V
// 检查过期
if exp, ok := c.expires[key]; ok {
if time.Now().After(exp) {
return zero, false
}
}
val, ok := c.items[key]
return val, ok
}
// 使用
userCache := NewCache[string, *User](5 * time.Minute)
userCache.Set("user:1", &User{Name: "小徐"})
user, ok := userCache.Get("user:1")
泛型 vs 接口 vs 代码生成
| 方案 | 类型安全 | 性能 | 可读性 | 复杂度 |
|---|
| 泛型 | ✓ 编译时 | 高 | 高 | 低 |
| interface{} | ✗ 运行时 | 中(有装箱) | 低 | 低 |
| 接口多态 | ✓ 编译时 | 高 | 高 | 中 |
| 代码生成 | ✓ 编译时 | 最高 | 中 | 高(需要生成步骤) |
| 场景 | 推荐 |
|---|
| 通用数据结构(Stack/Queue/Set/Cache) | 泛型 |
| 通用工具函数(Map/Filter/Reduce) | 泛型 |
| 需要多种类型支持(如 sort.Slice) | 泛型 |
| 不同类型有不同实现逻辑 | 接口 |
| 需要极致性能 | 代码生成 |
| 类型安全不重要的简单脚本 | interface{} |
泛型限制
| 限制 | 说明 | 替代方案 |
|---|
| 不能有泛型方法 | 只有函数和类型可以泛型,方法不行 | 泛型函数包装 |
| 不能在类型约束中引用自身 | type T interface{ ~T } 不合法 | 使用具体类型 |
| 不能用泛型类型做类型断言 | v.(T) 中 T 必须是具体类型 | type switch |
| 零值不能直接获取 | 不能写 var zero T(T 可以是 any) | var zero T 实际可以,但要注意 nil |
| 不能直接做算术运算 | 约束中没有 + 运算符 | 自定义约束接口 |
// ❌ 方法不能有自己的类型参数
type Container struct{}
func (c Container) Get[T any](key string) T { ... } // 编译错误
// ✓ 用泛型函数包装
type Container struct{}
func Get[T any](c Container, key string) T { ... }
slices 和 maps 标准库(Go 1.21+)
import "slices"
import "maps"
// slices 包:泛型 slice 操作
s := []int{3, 1, 4, 1, 5, 9, 2, 6}
slices.Sort(s) // [1, 1, 2, 3, 4, 5, 6, 9]
slices.SortFunc(s, func(a, b int) int { return b - a }) // 降序
slices.Contains(s, 5) // true
slices.Index(s, 4) // 索引
slices.Reverse(s) // 原地反转
slices.Max(s) // 最大值
slices.Min(s) // 最小值
slices.BinarySearch(s, 5) // 二分查找
// maps 包:泛型 map 操作
m := map[string]int{"a": 1, "b": 2, "c": 3}
keys := maps.Keys(m) // []string{"a", "b", "c"}(顺序随机)
values := maps.Values(m) // []int{1, 2, 3}
maps.Copy(m, map[string]int{"d": 4}) // 合并
maps.DeleteFunc(m, func(k string, v int) bool { return v < 2 }) // 条件删除
m2 := maps.Clone(m) // 浅拷贝
maps.Equal(m, m2) // true
cmp 标准库(Go 1.21+)
import "cmp"
// cmp.Compare:返回 -1, 0, 1
cmp.Compare(1, 2) // -1
cmp.Compare(2, 2) // 0
cmp.Compare(3, 2) // 1
// cmp.Or:返回第一个非零值
name := cmp.Or(inputName, "default") // 如果 inputName 为 "",返回 "default"