在这篇文章中,我们展示了如何在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教程。