Go语言之前一直被吐槽没有泛型,随着1.18版本的发布,Go语言也开始支持泛型了。本文主要简单了解一下什么是泛型,在代码中如何实现以及应用。
1. 泛型的定义 
  
     
泛型是一种编程语言的特性,允许在编写代码时不指定具体的数据类型,而在运行时动态确定,常用于通用算法或者数据容器存储。
例如有这么一个需求,分别对一组int类型和float类型数组的元素进行求和,通过泛型,我们可以实现只编写一个函数,可同时适用于int类型和float类型数组的求和。这种特性使得我们能够编写更加灵活、通用的代码,而不必为每种数据类型编写相似的逻辑。
2. 在代码中的使用 
  
     
2.1 一般写法 
  
     
2.1.1 格式 
  
     
1
 2
 3
  
func   函数名 [ 泛型名称   泛型约束 ]( 参数列表 )   返回值   { 
      函数内容 
 }  
 
2.1.2 例子 
  
     
1
 2
 3
  
func   Pri [ T   any ]( input   T ){ 
 	 fmt . Println ( input ) 
 }  
 
如上,Pri是一个打印传入参数的方法,[T any]表示泛型名称为T,可以是任何(any)类型。
2.2 类型约束 
  
     
以上面的泛型的定义中描述的内容为例子,实现一个适用于int类型和float类型数组求和的方法:
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
  
func    Sum [ T   int | float64 ]   ( slice   [] T )   T   { 
      var   sum   T 
      for   _ ,   v   :=   range   slice   { 
          sum   +=   v 
      } 
      return   sum 
 } 
 
 func   main ()   { 
      a   :=   [] int { 1 ,   2 ,   3 } 
      fmt . Println ( Sum ( a )) 
      b   :=   [] float64 { 1.2 ,   2.3 ,   3.4 } 
      fmt . Println ( Sum ( b )) 
 } 
 //结果 
 //6 
 //6。9  
 
其中[T int|float64]表示泛型T可以为int类型或者float64类型,这个是类型约束,表示使用时参数需要在约束条件内。
2.2.1 接口作为泛型约束 
  
     
除了以上写法之外,可以在结构体中定义对应的类型约束:
1
 2
 3
 4
 5
 6
 7
  
type   Type   interface { 
 	 int | string | float64 
 } 
 
 func   Pri [ T   Type ]( input   T ){ 
 	 fmt . Println ( input ) 
 }  
 
[T Type]表示约束为int、string、float64都可,这个适用于已经确定哪一些类型会经常作为泛型约束。
2.3 自定义泛型类型 
  
     
有些时候会针对一些特定类型进行合并,为之后参数扩展做准备,此时可以这样写:
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
  
type   MyInt   int 
 type   Type   interface { 
      ~ int | string | float64 
 } 
 func   Pri [ T   Type ]( input   T ){ 
      fmt . Println ( input ) 
 } 
 var   d   MyInt 
 d   =   88 
 Pri ( d ) 
 // 88  
 
type MyInt int表示MyInt是基于int类型定义的,~int表示任何基于int类型定义的参数,前后呼应。注意~只能加在int,string等基本类型前,自定义的类型不适用。
3. 泛型的大致原理 
  
     
Go的泛型实际上有点类似java的方法重载,在同一个类中,可以存在多个方法名相同,参数类型不同,参数个数不同,或者两者都不同的函数。
编译器在编译泛型函数时只生成一份函数副本,通过新增一个字典参数来供调用方传递类型参数(Type Parameters),这样就可以用一个函数实例支持多种类型参数。
这种实现方式称为字典传递(Dictionary passing)。
Go 实现泛型的方式,就是在编译阶段,通过将类型信息以字典的方式传递给泛型函数。当然这个字典不仅包含了类型信息,还包含了此类型的内存操作函数,如 make/len/new 等。
4. 1.18版本前的泛型实现方式 
  
     
在Go语言中,1.18前还没有原生支持泛型的语法,不过可以使用一些技巧来实现类似泛型的功能。
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
  
package   main 
  
 import   ( 
      "fmt" 
 ) 
  
 func   Sum ( a   ... interface {})   ( sum   interface {})   { 
      for   _ ,   v   :=   range   a   { 
          switch   v   :=   v .( type )   { 
          case   int : 
              if   sum   ==   nil   { 
                  sum   =   0 
              } 
              sum   =   v   +   sum .( int ) 
          case   float64 : 
              if   sum   ==   nil   { 
                  sum   =   0.0 
              } 
              sum   =   v   +   sum .( float64 ) 
          // 你可以继续添加其他类型的处理 
          default : 
              panic ( "unsupported type" ) 
          } 
      } 
      return 
 } 
  
 func   main ()   { 
      fmt . Println ( Sum ( 1 ,   2 )) 
      fmt . Println ( Sum ( 1.0 ,   2.0 )) 
 }  
 
 1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
  
package   main 
  
 import   ( 
      "fmt" 
      "reflect" 
 ) 
  
 func   Sum ( a   interface {})   ( sum   interface {})   { 
      v   :=   reflect . ValueOf ( a ) 
      if   v . Kind ()   !=   reflect . Slice   { 
          panic ( "should be a slice" ) 
      } 
  
      for   i   :=   0 ;   i   <   v . Len ();   i ++   { 
          if   sum   ==   nil   { 
              sum   =   0 
          } 
          sum   =   v . Index ( i ). Interface ()   +   sum 
      } 
      return 
 } 
  
 func   main ()   { 
      s   :=   [] int { 1 ,   2 ,   3 ,   4 } 
      fmt . Println ( Sum ( s )) 
 }  
 
使用代码生成工具实现泛型,这个实际上就是多写几个参数不同的方法 
 
4.1 泛型与interface的性能对比测试 
  
     
以使用interface{}实现泛型为例子,那么已经有类似的方法实现泛型的效果了,那么为什么还需要泛型。
个人觉的除了有规范开发格式的效果之外,性能上也存在一定差异,例如interface{}实现泛型效果,因为其中有断言的过程,会额外增加性能上的开销,以下是网上一个测试interface实现泛型和官方提供的泛型函数在性能上的对比,大家可以参考一下:
Go:泛型与interface{}的基准测试比较,性能解析 
5. 参考文章 
  
     
Go泛型的理解和使用小结 
B站视频:【GO语言】泛型的常用功能介绍与实例示范