Golang 泛型

Golang 泛型
泛型是一种独立于使用的特定类型编写代码的方式。使用其可以编写出适用于任意同种类型约束的函数和结构体。
Golang 的泛型特性,实际上是为 Golang 语言带来三个新的重要内容:
- 函数和结构体定义支持类型参数;
- 将 interface 定义为类型集合,也包含没有提供方法的类型;
- 类型推断,允许在调用函数时省略类型参数。
类型参数 在golang中类型参数的可以在函数或者自定义类型时一起声明,语法则是用方括号包围的参数列表,与普通的参数列表比较相似。
package main
import "fmt"
//在定义结构体类型时可以加入参数列表
type Message[T interface{}] struct {
Id string
Value T
}
//在声明函数时也可以增加参数列表
func NewMessage[T interface{}](id string, value T) Message[T] {
return Message[T]{
Id: id,
Value: value,
}
}
func main() {
message := NewMessage("unique-1", 8)
fmt.Println(message)
}
类型集合
在 1.18 版本之前,对于 interface
理解认知是:所有实现接口方法的类型都实现了该接口,即对应定义了方法集合;随着泛型特性的加入,
对于它,需要从另外一个角度理解:接口定义了一组类型,即实现这些方法的类型,即对应定义了类型集合;如下:
type MsgV interface {
int | float32 | float64 | ~string
}
//在定义结构体类型时可以加入参数列表
type Message[T MsgV] struct {
Id string
Value T
}
//在声明函数时也可以增加参数列表,函数参数类型推断,简化代码的编写
func NewMessage[T MsgV](id string, value T) Message[T] {
return Message[T]{
Id: id,
Value: value,
}
}
~string
表示底层类型为string
的所有类型集合,即string
类型本身,以及用它来声明的所有类型,如:type CString string
;any
为了方便增加了空接口类型别名,如:[T interface{}]
可以用[T any]
代替了,易于代码读写
类型推断
函数类型推断 编译器可以函数中的实参类型推断出函数类型参数的类型,这样编写的代码看起来和没有使用泛型特性一样,保持代码的简洁性。
func worker[T MsgV](id int, jobs <-chan Message[T], rs chan<- Message[T]) {
for job := range jobs {
fmt.Printf("%v: %v\n", job.Id, job.Value)
time.Sleep(2 * time.Second)
rs <- job
}
}
func main() {
const NUM_JOBS = 5
jobs := make(chan Message[int])
rs := make(chan Message[int])
for w := 1; w <= 3; w++ {
go worker(w, jobs, rs)
}
go func() {
for i := 1; i <= NUM_JOBS; i++ {
jobs <- NewMessage(fmt.Sprintf("%d", i), i+1) // 函数参数类型推断,简化代码的编写
}
close(jobs)
}()
for j := 1; j <= NUM_JOBS; j++ {
fmt.Println(<-rs)
}
}
约束类型推断
约束类型推断从类型参数约束中推到出类型参数类型,当其中一个类型参数的类型参数已知时,约束可用于推断另外一个类型参数的类型,如,S
是某类型的切片类型,E
则可以推断为该切片的元素类型。
type Point []int
func ScaleV1[E constraints.Integer](s []E, c E) []E {
r := make([]E, len(s))
for i, v := range s {
r[i] = v * c
}
return r
}
//约束类型推断从类型参数约束中推导出类型参数的类型
func ScaleV2[S ~[]E, E constraints.Integer](s S, c E) S {
r := make([]E, len(s))
for i, v := range s {
r[i] = v * c
}
return r
}
func main() {
var p Point
for j := 1; j <= 5; j++ {
p = append(p, j)
}
fmt.Printf("%v\n", ScaleV1(p, 3))
fmt.Printf("%v\n", ScaleV2(p, 4))
}
结论
- 正确的运用泛型特性可以让编写程序更加高效;
- 1.18 版本中的泛型特性已经基本可以符合大部分场景使用,现在是问题是当需要在生产环境下使用时,需要经历充分的测试。
Publish on 2022-05-02,Update on 2025-02-10