go语言中的泛型 | 青训营

92 阅读2分钟

go语言泛型

go语言中的泛型

Go 1.18版本增加了对泛型的支持。泛型是Go自第一个开源版本以来所做的最大改变。

泛型是一种编写代码的方式,它独立于正在使用的特定类型。函数和类型现在可以被写成使用一组类型中的任何一种。

Type Parameters 类型参数

一个比大小函数例子

func SMin(x, y int) int {
	if x < y {
		return x
	}
	return y
}

如果我们希望这个函数不仅仅可以用来比较int类型的参数,我们可以通过添加一个类型参数列表来让这个函数通用化-使其适用于不同的类型:

import "golang.org/x/exp/constraints"

func GMin[T constraints.Ordered](x, y T) T {
    if x < y {
        return x
    }
    return y
}

其中的constraints.Ordered作为一个接口,定义如下:

// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
	Integer | Float | ~string
}

他是多种数据类型的一个集合,可以让函数的参数多样化。这样我们无需实现适用于不同类型的函数,实现泛型。

泛型的语法

实际上,在golang中,泛型是从接口中演化而来的。

type Man interface {
	Say()
}

type student struct{}

// student实现Say方法
func (s student) Say() {}

// 面向接口编程
func f1(a Man) {}

//强用泛型,多此一举
func f2[T Man](a T) {}

其中的中括号里面,T相当于在这个函数中给Man起了一个别名,实际上T和Man是等价的。

在我们开始提到的例子中,有这样一种写法:

// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

这里实际上实现了一个联合类型,也就是说Singned包含其中声明的类型, ~int这个语法表示包含一切底层是int32的类型,例如rune

泛型类型

上面,我们介绍了泛型函数:即函数可以接受任意类型。注意和 interface{} 这样的任意类型区分开,泛型中的类型,在函数内部并不需要做任何类型断言和反射的工作,在编译期就可以确定具体的类型。

我们知道,Go 支持自定义类型,比如标准库 sort 包中的 IntSlice:

type IntSlice []int

此外,还有 StringSlice、Float64Slice 等,一堆重复代码。如果我们能够定义泛型类型,就不需要定义这么多不同的类型了。比如:

type Slice[T any] []T

能看懂吧。

在使用时,针对 int 类型,就是这样:

x := Slice[int]{1, 2, 3}

如果作为函数参数,这么使用:


func PrintSlice[T any](b Slice[T])

如果为这个类型定义方法,则是这样:

func (b Slice[T]) Print()

也就是说,Slice[T] 作为整体存在。

当然,泛型类型也可以做类型约束,而不是 any 类型:

type Slice[T comparable] []T