【Go专家编程——泛型】

泛型

泛型是程序设计语言的一种风格或范式,它允许程序员在编写代码时使用一些在以后才指定的类型,在实例化时作为参数指明这些类型。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)
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值