首页 / Go 语言入门教程 / 泛型类型与方法

Go 语言入门教程

泛型类型与方法

本教程共 80 篇 · 第 49 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程泛型泛型类型泛型类型别名自引用约束Go 1.24Go 1.26

49. 泛型类型与方法

本节目标:学会定义泛型结构体、泛型切片等类型,给泛型类型加方法,了解 1.24 泛型类型别名和 1.26 自引用约束。

泛型结构体

不只是函数,类型也可以有类型参数。最常见的是泛型结构体:

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
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

func main() {
    // 整数栈
    intStack := &Stack[int]{}
    intStack.Push(1)
    intStack.Push(2)
    v, _ := intStack.Pop()
    fmt.Println(v) // 2

    // 字符串栈
    strStack := &Stack[string]{}
    strStack.Push("hello")
    s, _ := strStack.Pop()
    fmt.Println(s) // hello
}

注意几个要点:

  1. 类型声明 type Stack[T any] struct{...} 带类型参数
  2. 方法的接收者要带上类型参数:func (s *Stack[T]) Push(item T)
  3. 泛型零值用 var zero T 声明,T 的零值取决于具体类型
Note

创建泛型类型实例时通常需要指定类型实参:Stack[int]{}。但如果编译器能推断出来,也可以省略。

泛型切片

给切片类型加上泛型参数:

type List[T any] []T

func main() {
    nums := List[int]{1, 2, 3}
    nums = append(nums, 4)
    fmt.Println(nums) // [1 2 3 4]

    strs := List[string]{"a", "b"}
    fmt.Println(strs) // [a b]
}

泛型切片继承了底层切片的所有操作(append、len、cap、range 等),还能加自己的方法:

func (l List[T]) Filter(f func(T) bool) List[T] {
    var result List[T]
    for _, v := range l {
        if f(v) {
            result = append(result, v)
        }
    }
    return result
}

func main() {
    nums := List[int]{1, 2, 3, 4, 5}
    evens := nums.Filter(func(n int) bool { return n%2 == 0 })
    fmt.Println(evens) // [2 4]
}

泛型 map

type Map[K comparable, V any] map[K]V

func main() {
    m := Map[string, int]{
        "apple":  5,
        "banana": 3,
    }
    fmt.Println(m["apple"]) // 5
}

K 必须是 comparable,因为 map 的 key 必须可比较。

泛型方法

Go 的方法不能有自己的类型参数。也就是说,方法的类型参数只能来自接收者:

type Box[T any] struct {
    Value T
}

// OK:用了接收者的类型参数 T
func (b *Box[T]) Get() T {
    return b.Value
}

// 编译错误:方法不能有自己的类型参数
// func (b *Box[T]) Convert[U any]() U {
//     ...
// }
Warning

Go 不支持给方法加额外的类型参数。如果你需要这种功能,改成泛型函数:

// 不行:方法带额外类型参数
// func (b *Box[T]) Convert[U any]() U

// 行:用泛型函数替代
func Convert[T any, U any](b *Box[T], f func(T) U) U {
    return f(b.Value)
}

泛型接口

接口也可以有类型参数:

type Container[T any] interface {
    Add(item T)
    Get(index int) (T, bool)
    Size() int
}

// 实现
type ArrayList[T any] struct {
    items []T
}

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

func (a *ArrayList[T]) Get(index int) (T, bool) {
    if index < 0 || index >= len(a.items) {
        var zero T
        return zero, false
    }
    return a.items[index], true
}

func (a *ArrayList[T]) Size() int {
    return len(a.items)
}

func main() {
    var c Container[int] = &ArrayList[int]{}
    c.Add(42)
    v, ok := c.Get(0)
    fmt.Println(v, ok) // 42 true
}

Go 1.24:泛型类型别名

Go 1.24 开始支持泛型类型别名。你可以给泛型类型起别名,别名也带类型参数:

package main

import "fmt"

// 泛型类型
type OrderedMap[K comparable, V any] struct {
    data map[K]V
}

// Go 1.24:泛型类型别名
type StringMap[V any] = OrderedMap[string, V]

func main() {
    // 用别名,只写一个类型参数
    m := StringMap[int]{
        data: map[string]int{"a": 1},
    }
    fmt.Println(m.data["a"]) // 1
}

StringMap[V]OrderedMap[string, V] 的别名,固定了 K 为 string,V 还是泛型。使用时只需指定一个类型参数。

Note

泛型类型别名是 Go 1.24(2025 年 2 月)引入的。之前的版本不支持给泛型类型起别名。

Go 1.26:自引用约束

Go 1.26 允许泛型类型在自己的类型参数列表中引用自身。这在定义递归类型约束时非常有用:

package main

import "fmt"

// Go 1.26:自引用约束
type Adder[A Adder[A]] interface {
    Add(A) A
}

// 实现
type IntAdder int

func (a IntAdder) Add(b IntAdder) IntAdder {
    return a + b
}

func algo[A Adder[A]](x, y A) A {
    return x.Add(y)
}

func main() {
    result := algo(IntAdder(3), IntAdder(5))
    fmt.Println(result) // 8
}

Adder[A Adder[A]] 表示 A 必须是一个实现了 Add(A) A 的类型,而且 A 就是它自己。这种自引用约束在定义代数数据结构(如 F-代数)时特别有用。

Tip

自引用约束是 Go 1.26(2026 年 2 月)的新特性。之前版本写 Adder[A Adder[A]] 会编译错误。这是高级特性,日常开发用得不多,但在某些抽象数据结构设计中很有价值。

小结

泛型类型的要点:

  • 泛型结构体/切片/map:type Name[T any] ...
  • 方法的类型参数只能来自接收者,方法不能有额外类型参数
  • 泛型零值用 var zero T 声明
  • Go 1.24 支持泛型类型别名
  • Go 1.26 支持自引用约束

下一章讲类型推断规则和泛型使用的最佳实践。