首页 >工作总结 > 内容

Go 1.18 系列篇(二):一文掌握泛型的使用

2023年9月22日 00:21

微信搜索 “写点代码的明哥”,关注更多关于 Go 与 K8S 的干货文章

系列导读:
1、Go 1.18 系列篇(一):如何升级 Go 1.18 ?

泛型,可以说是 Go 这几年来最具争议的功能,应该没人有意见吧?

其实 Go 在早前的 Beta 版本中,就提供了对泛型的支持,但还不够成熟,直到 Go 1.18 才是支持泛型的正式版本。

下面我学习了官方关于泛型的文档之后,将学习的心得总结分享给大家

1. 非泛型的写法

现有一个 map ,我们需要实现一个函数,来遍历该 map 然后将 value 的值全部相加并返回。

而由于这个 map 的 value 可以是任意类型的数值,比如 int64, float64

于是为了接收不同类型的 map,我们就得定义多个函数,这些函数 除了入参类型及返回值类型不同外,没有任何不同

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}

2. 用泛型的写法

若是以代码行数来定义工作量,我可不希望泛型的出现,但从另一方面来讲,这种代码横看竖看都让人非常不舒服。

同样的需求,在有了泛型之后,写法就变得简洁许多, 两个函数可以简化成一个函数

func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {    var s V    for _, v := range m {        s += v    }    return s}

在这个函数中,它比常规的函数多了 [K comparable, V int64 | float64] 这么一段代码,这便是 泛型新增的语法,位于函数名与形参之间。

我来解释下这段 “代码”:

  • K 和 V 你可以理解为类型别名,在中括号之间进行定义,作用域也只在此函数内,可以在形参、函数主体、返回值类型 里使用
  • comparable 是 Go 语言预声明的类型,是那些可以比较(可哈希)的类型的集合,通常用于定义 map 里的 key 类型
  • int64 | float64 意思是 V 可以是 int64 或 float64 中的任意一个
  • map[K]V 就是使用了 K 和 V 这两个别名类型的 map

有了泛型函数的定义,那如何调用该函数?

调用方式还是跟普通函数一样,只是在函数名和实参之间,可以再次使用中括号来指明上面的 K 和 V 分别是什么类型?

func main() {    // Initialize a map for the integer values    ints := map[string]int64{        "first": 34,        "second": 12,    }    // Initialize a map for the float values    floats := map[string]float64{        "first": 35.98,        "second": 26.99,    }    fmt.Printf("Generic Sums: %v and %v\n",        SumIntsOrFloats[string, int64](ints),        SumIntsOrFloats[string, float64](floats),    )}

最后使用 go run 去跑一下,结果正常输出

3. 简化泛型写法

3.1 类型自动推导

在调用大部分的泛型函数时,中括号里的内容,是可以省略不写的,而这个不写的前提是,编译器有办法根据你的实参及形参来自动推导出泛型函数中 别名类型对应的类型(在上例中就是 K 和 V)。

而在上面的例子中,刚好是满足的,于是泛型函数的调用就可以简化成这样

    fmt.Printf("Generic Sums: %v and %v\n",        SumIntsOrFloats(ints),        SumIntsOrFloats(floats),    )

3.2 使用类型别名

上面的 V 使用 int64 | float64 这样的写法来表示 V 可以是其中的任意一种类型。

若这个 V 用得比较多呢?可以考虑用 type 来事先定义别名,一来方便复用,二来在泛型函数使用更加简短的别名,可以提高代码可读性

type Number interface {    int64 | float64}

然后泛型函数的定义就可以简化成下面这样

func SumNumbers[K comparable, V Number](m map[K]V) V {    var s V    for _, v := range m {        s += v    }    return s}

4. 写在最后

在去年,其实就通过其他人的文章中事先了解到了 Go 泛型的写法,给我的第一印象是,函数的定义变得更复杂,可读性也越来越差,一时间我也有点难以接受。

不过经过自己试用后,情况倒没有我想象的那么糟糕!新版没有改变原有函数的定义与调用,若你没有使用泛型,那么有没有泛型对你来说没有区别。

但即使你有想法需要用到泛型,我也相信这种的不适感会在时间的流逝中慢慢淡化。

由于泛型改动太大,目前 Goland 的 EAP 对泛型的支持还有很多 bug ,该检测出的语法错误没有被检测出来,正确的语法被视为错误标红之类,想用在 Goland 中使用泛型,需要有这个意识​,否则严重影响写代码的流畅度,容易被错误的提示打断了思路。


参考文章:https://blog.csdn.net/weixin_36338224/article/details/123669701

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时候联系我们修改或删除,在此表示感谢。

特别提醒:

1、请用户自行保存原始数据,为确保安全网站使用完即被永久销毁,如何人将无法再次获取。

2、如果上次文件较大或者涉及到复杂运算的数据,可能需要一定的时间,请耐心等待一会。

3、请按照用户协议文明上网,如果发现用户存在恶意行为,包括但不限于发布不合适言论妄图

     获取用户隐私信息等行为,网站将根据掌握的情况对用户进行限制部分行为、永久封号等处罚。

4、如果文件下载失败可能是弹出窗口被浏览器拦截,点击允许弹出即可,一般在网址栏位置设置

5、欢迎将网站推荐给其他人,网站持续更新更多功能敬请期待,收藏网站高效办公不迷路。

      



登录后回复

共有0条评论