Golang中使用泛型

在这篇文章中,我们展示了如何在Golang中使用泛型。

$ go version
go version go1.18.1 linux/amd64

我们使用Go版本1.18。

使用泛型,我们可以编写可用于各种类型的代码。可以编写函数和其他类型来使用一组类型中的任何一种。泛型有助于减少代码重复。

Go使用泛型的参数类型。它们在方括号[]内指定。

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

按照惯例,泛型参数类型用大写字母指定,通常是TKV

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泛型省略类型

在很多情况下,我们可以在调用泛型函数时省略类型。编译器会根据函数参数推断类型。

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]

通用联合类型

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

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的类型集。

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

通用过滤器函数

过滤器函数处理一个集合并生成一个新集合,该集合恰好包含给定谓词返回true的那些元素。

在下一个示例中,我们将创建过滤器函数的通用版本。

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)
}

在程序中我们使用了一个通用的过滤函数来过滤字符串和整数。

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
}

过滤器函数构建一个新切片,其中仅包含满足给定条件的元素。该函数适用于具有约束any的参数类型T。它以一个集合和一个谓词函数作为参数。我们在每个元素上调用谓词,如果它与谓词的条件匹配,则将其添加到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]

通用ForEach函数

在下一个程序中,我们创建一个通用的ForEach函数。

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中的泛型。

列出所有Go教程。

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏