← 泛型类型 | 类型推断 →

类型约束 - 泛型约束详解

类型约束限制泛型类型参数的范围,确保类型支持特定操作。掌握类型约束是编写类型安全泛型代码的关键。

📌 核心概念

🔗

内置约束

any/comparable

constraints 包
📝

自定义约束

接口定义约束

type constraint

联合类型

类型并集

A | B
🔧

底层类型

~T 语法

~int

内置约束

📖 Go 内置约束

package main

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

// any: 任何类型 (interface{} 的别名)
func Print[T any](v T) {
    fmt.Println(v)
}

// comparable: 可比较的类型 (可用 == 和 != 比较)
// 包括:bool, 数值类型,string, pointer, channel, interface
func Contains[T comparable](slice []T, target T) bool {
    for _, v := range slice {
        if v == target {
            return true
        }
    }
    return false
}

// constraints.Ordered: 有序类型 (Go 1.21+ 内置)
// 包括:int, int8, ..., float32, float64, string
func Min[T constraints.Ordered](a, b T) T {
    if a < b {
        return a
    }
    return b
}

// constraints.Integer: 整数类型
// int, int8, int16, int32, int64, uint, ...
func Abs[T constraints.Integer](v T) T {
    if v < 0 {
        return -v
    }
    return v
}

// constraints.Float: 浮点数类型
// float32, float64
func Round[T constraints.Float](v T) T {
    return T(int(v + 0.5))
}

// constraints.Signed: 有符号整数
// constraints.Unsigned: 无符号整数

func main() {
    Print(42)
    Print("hello")
    
    fmt.Println(Contains([]int{1, 2, 3}, 2))
    fmt.Println(Min(10, 20))
    fmt.Println(Abs(-5))
}

自定义约束

📝 定义自定义约束

package main

import "fmt"

// 约束 1: 实现 Stringer 接口的类型
type Stringer interface {
    String() string
}

func PrintStringer[T Stringer](v T) {
    fmt.Println(v.String())
}

// 实现约束的类型
type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s(%d)", p.Name, p.Age)
}

// 约束 2: 联合类型 (类型并集)
type Number interface {
    constraints.Integer | constraints.Float
}

func Add[T Number](a, b T) T {
    return a + b
}

// 约束 3: 带方法的联合类型
type Adder interface {
    constraints.Integer | constraints.Float
    ~[]int // 底层类型是 []int
}

// 约束 4: 数值类型约束
type Numeric interface {
    constraints.Integer | constraints.Float | constraints.Complex
}

func Sum[T Numeric](nums ...T) T {
    var sum T
    for _, n := range nums {
        sum += n
    }
    return sum
}

func main() {
    p := Person{Name: "Alice", Age: 30}
    PrintStringer(p)
    
    fmt.Println(Add(10, 20))
    fmt.Println(Add(3.14, 2.71))
    
    fmt.Println(Sum(1, 2, 3, 4, 5))
}

~ 底层类型语法

📖 ~T 表示底层类型

package main

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

// ~int: int 及其底层类型为 int 的所有类型
type IntLike interface {
    ~int
}

// 自定义类型,底层类型是 int
type MyInt int

func (m MyInt) Double() MyInt {
    return m * 2
}

// 使用 ~int 约束
func Double[T IntLike](v T) T {
    return v + v
}

// 更实用的例子:带方法的约束
type StringLike interface {
    ~string
}

func ToUpper[T StringLike](s T) T {
    // 这里需要类型转换才能使用 strings.ToUpper
    return s
}

// 实际使用:切片类型约束
type SliceOf[T any] interface {
    ~[]T
}

func Reverse[T any, S SliceOf[T]](s S) S {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
    return s
}

func main() {
    // int
    fmt.Println(Double(5)) // 10
    
    // MyInt (底层类型是 int)
    fmt.Println(Double(MyInt(5))) // 10
    
    // 反转切片
    nums := []int{1, 2, 3, 4, 5}
    fmt.Println(Reverse(nums)) // [5 4 3 2 1]
}

约束组合

📝 组合多个约束

package main

import (
    "fmt"
    "golang.org/x/exp/constraints"
)

// 组合约束:有序 + 可比较
type OrderedComparable interface {
    constraints.Ordered
    constraints.Comparable
}

// 使用组合约束
func FindMin[T OrderedComparable](slice []T) T {
    if len(slice) == 0 {
        var zero T
        return zero
    }
    
    min := slice[0]
    for _, v := range slice[1:] {
        if v < min {
            min = v
        }
    }
    return min
}

// 多约束泛型函数
func Process[
    T constraints.Integer | constraints.Float,
    U constraints.Ordered,
](nums []T, names []U) {
    // T 必须是整数或浮点数
    // U 必须是有序类型
    for i, n := range nums {
        fmt.Printf("%d: %v\n", i, n)
    }
}

func main() {
    nums := []int{3, 1, 4, 1, 5}
    fmt.Println(FindMin(nums)) // 1
    
    strs := []string{"banana", "apple", "cherry"}
    fmt.Println(FindMin(strs)) // "apple"
}

最佳实践

✅ 类型约束使用建议

  • 优先内置约束: any, comparable, constraints.Ordered
  • 最宽松约束: 使用满足需求的最宽松约束
  • 清晰命名: 约束接口名称应清晰表达意图
  • 避免过度约束: 不要限制不必要的操作
  • 文档说明: 为自定义约束添加清晰文档