在这篇文章中,我们展示了如何在Golang中使用泛型。
$ go version go version go1.18.1 linux/amd64
我们使用Go版本1.18。
使用泛型,我们可以编写可用于各种类型的代码。可以编写函数和其他类型来使用一组类型中的任何一种。泛型有助于减少代码重复。
Go使用泛型的参数类型。它们在方括号[]内指定。
func Println[T any](v T) {
fmt.Println(v)
}
按照惯例,泛型参数类型用大写字母指定,通常是T、K或V。
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教程。
