泛型
泛型是程序设计语言的一种风格或范式,它允许程序员在编写代码时使用一些在以后才指定的类型,在实例化时作为参数指明这些类型。Go在1.18引入了泛型
1. 泛型的使用
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
//使用[]来声明泛型的类型
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
//显示调用泛型函数,指明K、V的具体类型
SumIntsOrFloats[string,int64](ints)
//隐式调用泛型函数
SumIntsOrFloats(ints)
编译器能够推导出参数类型的前提是该函数存在入参,编译器通过传入的变量和函数的参数声明可以推导出参数类型,但对于没有函数参数的泛型函数来说,编译器无法进行推导,也就无法实例化泛型函数,所以此时必须显式调用并制定类型参数。
2. 泛型总览
2.1 函数的泛化
Go语言函数通过使用方括号[]表示类型的泛型参数
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V
2.2 类型的泛化
泛型同样扩展了类型的表示方式,允许在创建自定义类型时也能够接受一个使用方括号([])表示的类型参数,
函数类型与put类型一样,同样允许为其定义方法,但其receiver类型必须带上类型参数
// Vector是可容纳任意类型的容器
type Vector[T any] []T
//推荐的写法
func(v *Vector[T]) Push(x T){
*v = append(*v, x)
}
//存在,但不推荐
func(v *Vector[Y]) Push(x Y){
*v = append(*v, x)
}
//存在,但不推荐
func(v *Vector[_]) Push(x _){
*v = append(*v, x)
}
这里的any等同于interface{}。
在实例化一个泛型类型时,必须指定类型参数(因为编译器无法自动推导)
var intVec Vector[int]//实例化为整型容器
var stringVec Vector[string]//实例化为字符串容器
3.类型约束
3.1 类型集合
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V
我们继续使用前面的函数来说:
在函数的类型参数中:
- K的类型先定为comparable
- V的类型限定为int64|float64
- comparable为interface类型
- int64|float为组合类型
- 两者都代表一个类型集合,用于约束泛化的范围
3.2 interface类型集合
interface可以用来表示任何类型集合。
1)内置interface类型集合
comparable和any两个interface都是跟随泛型一起被引入的内置interface类型
type comparable interface{ comparable }
表示可比较类型的集合,仅能用于类型参数。
type any = interface{}
any不仅在类型参数中表示任意类型的集合,还可以在非泛型场景中作为interface{}的别名使用
2)自定义interface类型集合
除了内置的comparable和any两种类型可作为类型约束使用,用户还可以使用interface来定义类型集合。
- 任意类型元素(如int)
- 近似类型元素(使用~ 表示,如~64)
- 联合类型元素(使用|表示,如~ int |~int64)
- 如果interface类中使用了这三种元素的任意一种,那么这个interface只能用于泛型的类型参数
(1)任意类型元素
type Integer interface{
int
}
func SumInteger[T Integer](a T,b T) T {
return a+b
}
(2)近似类型元素
在使用interface声明类型集合时,可以使用~ <type>来指定一组类型,只要其底层类型为同一类型即包含在这个集合中。~ 之后必须是某个底层类型
type AnyString interface{
~string
}
func SayHi[T AnyString](name T){
fmt.Printf("Hi %s",name)
}
type MyString string
var s MyString = "John"
SayHi(s)//MyString的底层类型为string,可以实例化
(3)联合类型元素
如果希望把所有底层为有符号整型的类型组合在一起,则可以结合“~”来使用:
例如:
type SignedInteger interface{
~int | ~int8 | ~int16 | ~int32 | ~int64
}
3)interface类型集合运算
事实上interface支持按行指定多个子集合,这些子集合取交集形成最终的集合
type MyString interface {
~string//底层为string的集合
string//string单一类型
}
取交集后最终的类型集合只包含string一个类型。
4.泛型举例
4.1 MapKeys
任务:将map中的key全部取出形成列表
func MapKeys[K comparable, V any](m map[K]V)[]K{
r := make([]K,len(m))
for k := range m {
r = append(r,k)
}
return r
}
4.2 Set
这里利用map实现了一个Set
//set,底层使用map实现
type Set[T comparable] map[T]struct{}
//构造某个comparable类型的set
func MakeSet[T comparable]() Set[T]{
return make(Set[T])
}
//Add,添加元素V
func (s Set[T]) Add(v T){
s[v] = struct{}
}
//Delete,删除元素v
func (s Set[T]) Delete(v T){
delete(s,v)
}
//Contains,查询元素v是否存在
func (s Set[T]) Contains(v T)bool{
_,ok := s[v]
return ok
}
//Len,返回元素的个数
func (s Set[T]) Len() int{
return len(s)
}
//Iterator:遍历Set并逐个调用函数f
func (s Set[T]) Iterate(f func(T)){
for v := range s {
f(v)
}
}