Go 1.18 泛型
上篇文章提及Go 1.18是第一个支持泛型的版本,那么什么是泛型呢?泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
通常我们在写代码的时候,经常需要写很多重复的逻辑,一般这个时候我们就会使用函数来对其进行封装。但是由于Go是一种强类型语言,所以在定义和书写函数的时候需要在调用前标明类型。当然如果这一重复的逻辑只需要固定的类型,这样就足够了,但是很多时候我们需要不同的类型进行类似的逻辑,这样我们就会写很多相似的函数,总而言之不太优雅和简易。
看一个官网泛型示例
func main() { ints := map[string]int { "first": 12, "second": 13, } floats := map[string]float64 { "first": 12.00, "second": 13.00, } fmt.Println("ints 泛型求和:%v", SumIntsOrFloats(ints)) fmt.Println("floats 泛型求和:%v", SumIntsOrFloats(floats)) } func SumIntsOrFloats[K comparable, V int | float64 ](m map[K]V) V { var res V for _, v := range m { res += v } return res }
[K comparable, V int | float64 ]
这是泛型参数列表,它由两部分组成,其中K or V就是泛型参数,comparable or int or float64为泛型参数的约定或者就是泛型参数的类型;(m map[K]V)
这是泛型函数参数,它的定义就依赖于前面泛型参数列表的声明。
注意:comparable是golang新引入的预定义标识符,是一个接口,指代可以使用==或!=来进行比较的类型集合。comparable仅能用于泛型中的类型限定(type constraint)。
运行看下结果:
ints 泛型求和:%v 25 floats 泛型求和:%v 25
其中V可以用接口约束比如:
// 接口 type Number interface { int | float64 } func SumIntsOrFloats[K comparable, V Number ](m map[K]V) V { var res V for _, v := range m { res += v } return res }
运行结果和上面一样的。
泛型支持接口约束
package main import "fmt" // Hi 接口 type Hi interface { A | B Hi() } type A struct { Aa string Cc string } type B struct { Bb string Cc string } // A实现Hi接口 func (a A) Hi() { fmt.Println(a.Aa) } // B实现Hi接口 func (b B) Hi() { fmt.Println(b.Bb) } // 泛型函数 T类型是接口类型(Hi) func SayHi[T Hi](t T) { t.Hi() } func main() { a := A{"aa", "cc"} b := B{"bb", "cc"} SayHi(a) SayHi(b) }
运行结果:
aa bb
其实接口约束在泛型中比较常见,也比较容易理解,就是定义一个接口,其他类实现接口,但是因为要支持泛型,所以实现接口的类必须是接口的一部分,就像Hi中定义的那样。
小结
这只是Go泛型的冰山一角,想要了解很多泛型知识的同学还得到官网学习,泛型不是目的,写出真正优秀可维护的代码才是程序员孜孜不倦的唯一追求。官网参考:https://go.dev/ref/spec