ZetCode

Go 泛型

最后修改于 2025 年 5 月 5 日

在本文中,我们将介绍如何在 Golang 中使用泛型。

Go 中的泛型使开发人员能够编写灵活且可重用的代码,这些代码可以处理多种数据类型。通过使用泛型,可以以允许它们处理任何指定类型集的方式定义函数和类型,从而减少冗余并提高可维护性。

Go 通过参数化类型来实现泛型,这些类型定义在方括号 [] 内。这些类型参数允许开发人员在保持灵活性的同时指定约束。

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

在此示例中,Println 函数接受一个泛型类型参数 Tany 关键字用作约束,表示 T 可以是任何类型。调用时,该函数会打印其参数,同时保持类型安全。

根据约定,泛型参数类型用大写字母表示,最常用的是 T 表示通用类型,K 表示键,V 表示值。此标准提高了 Go 程序的可读性和一致性。

当设计需要类型灵活性但又要保持强类型和效率的库、数据结构和算法时,泛型尤其有用。

Go 泛型示例

在第一个示例中,我们定义了一个简单的泛型函数。

main.go
package main

import (
    "fmt"
)

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

func main() {
    Println[string]("an old falcon")
    Println[int](23)
    Println[float64](3.34)
    Println[bool](true)
    Println[[]int]([]int{1, 2, 3, 4, 5})
}

我们的自定义 Println 函数可以接受任何类型并打印它。

Println[string]("an old falcon")
Println[int](23)
Println[float64](3.34)
Println[bool](true)
Println[[]int]([]int{1, 2, 3, 4, 5})

我们打印字符串值、int64 值、float64 值、bool 值和整数切片值。

$ go run main.go
an old falcon
23
3.34
true
[1 2 3 4 5]

Go 泛型省略类型

在许多情况下,我们可以省略调用泛型函数时的类型。编译器将从函数参数推断出类型。

main.go
package main

import (
    "fmt"
)

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

func main() {
    Println("an old falcon")
    Println(23)
    Println(3.34)
    Println(true)
    Println([]int{1, 2, 3, 4, 5})
}

在程序中,我们在调用 Println 函数时省略了参数类型声明。

$ go run main.go
an old falcon
23
3.34
true
[1 2 3 4 5]

Go 泛型联合类型

我们可以将泛型类型参数限制为联合中的类型。

main.go
package main

import (
    "fmt"
)

func Println[T string | int](v T) {
    fmt.Println(v)
}

func main() {
    Println("an old falcon")
    Println(23)
    // Println(3.34)
    // Println(true)
    // Println([]int{1, 2, 3, 4, 5})
}

在程序中,Println 函数可以接受字符串或整数。

$ go run main.go
an old falcon
23

Go 泛型波浪线

~ 波浪线标记用于 ~T 的形式,表示底层类型为 T 的类型集合。

main.go
package main

import (
    "fmt"
)

type mystring string

func Println[T ~string | int](v T) {
    fmt.Println(v)
}

func main() {
    Println("an old falcon")
    Println(23)
    Println(mystring("rainy day"))
}

在程序中,我们有一个自定义的 mystring 类型。使用 ~string 语法,我们告诉编译器包含任何近似于 string 的类型。

$ go run main.go
an old falcon
23
rainy day

Go 泛型过滤函数

filter 函数处理一个集合,并生成一个新集合,其中只包含给定谓词返回 true 的元素。

在下一个示例中,我们创建了一个泛型版本的 filter 函数。

main.go
package main

import (
    "fmt"
    "strings"
)

func filter[T any](data []T, f func(T) bool) []T {

    fltd := make([]T, 0, len(data))

    for _, e := range data {
        if f(e) {
            fltd = append(fltd, e)
        }
    }

    return fltd
}

func main() {

    words := []string{"war", "cup", "water", "tree", "storm"}

    res := filter(words, func(s string) bool {
        return strings.HasPrefix(s, "w")
    })

    fmt.Println(res)

    vals := []int{-1, 0, 2, 5, -9, 3, 4, 7}

    res2 := filter(vals, func(e int) bool {
        return e > 0
    })

    fmt.Println(res2)
}

在程序中,我们使用泛型 filter 函数来过滤字符串和整数。

func filter[T any](data []T, f func(T) bool) []T {

    fltd := make([]T, 0, len(data))

    for _, e := range data {
        if f(e) {
            fltd = append(fltd, e)
        }
    }

    return fltd
}

filter 函数构建一个新切片,其中仅包含满足给定条件的元素。该函数作用于类型参数 T,约束为 any。它接受集合和谓词函数作为参数。我们对每个元素调用谓词,如果它满足谓词的条件,则将其添加到 fltd 切片中。

res := filter(words, func(s string) bool {
    return strings.HasPrefix(s, "w")
})

这里我们过滤掉所有以“w”开头的单词。

$ go run main.go
[war water]
[2 5 3 4 7]

Go 泛型 ForEach 函数

在下一个程序中,我们创建了一个泛型 ForEach 函数。

main.go
package main

import "fmt"

func ForEach[T any](data []T, f func(e T, i int, data []T)) {

    for i, e := range data {
        f(e, i, data)
    }
}

func main() {

    vals := []int{-1, 0, 2, 1, 5, 4}

    ForEach(vals, func(e int, i int, data []int) {

        fmt.Printf("e at %d: %d\n", i, e)
    })

    fmt.Println("-------------------------")

    words := []string{"sky", "forest", "word", "cup", "coin"}

    ForEach(words, func(e string, i int, data []string) {

        fmt.Printf("e at %d: %s\n", i, e)
    })
}

泛型 ForEach 函数将泛型切片和闭包函数作为参数。闭包用于对每个元素执行任务。

func ForEach[T any](data []T, f func(e T, i int, data []T)) {

    for i, e := range data {
        f(e, i, data)
    }
}

ForEach 函数中,我们使用 for 循环遍历泛型切片的元素,并对每个元素调用闭包。

ForEach(vals, func(e int, i int, data []int) {

    fmt.Printf("e at %d: %d\n", i, e)
})

在实际调用 ForEach 函数时,我们传递一个具有具体类型的闭包。元素类型为 int,索引类型为 int,集合类型为 int[]

$ go run main.go
e at 0: -1
e at 1: 0
e at 2: 2
e at 3: 1
e at 4: 5
e at 5: 4
-------------------------
e at 0: sky
e at 1: forest
e at 2: word
e at 3: cup
e at 4: coin

来源

开始使用泛型 - 教程

在本文中,我们涵盖了 Golang 中的泛型。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1400 多篇文章和 8 本电子书。我在教学编程方面拥有十多年的经验。

列出所有 Go 教程