Golang泛型的使用方法详解

 更新时间:2022年12月14日 09:07:27   作者:梦想画家  
这篇文章主要介绍了Golang中泛型的使用,Go和Python语言不同,处理不同数据类型非常严格。如Python可以定义函数带两个数值类型并返回较大的数值,但可以不严格指定参数类型为float或integer

Go 和Python语言不同,处理不同数据类型非常严格。如Python可以定义函数带两个数值类型并返回较大的数值,但可以不严格指定参数类型为float或integer。同样功能go1.18之前版本需要定义两个函数分别处理对应类型,通过泛型可以实现上面描述功能,无需为每种类型重复定义函数。

本文通过示例学习Go泛型,包括三个部分:非泛型函数,泛型函数,泛型类型约束。

泛型的作用

我们知道Go一些函数(如fmt.Println)使用空interface支持多种数据类型,但需要开发者写大量代码实现很多函数或方法支持多种自定义类型不是最佳选项,于是泛型登场,提供新的解决方案,无需使用接口和反射支持多种数据类型。

下面通过示例来说明:

package main
import (
    "fmt"
)
func Print[T any](s []T) {
    for _, v := range s {
        fmt.Print(v, " ")
    }
    fmt.Println()
}
func main() {
    Ints := []int{1, 2, 3}
    Strings := []string{"One", "Two", "Three"}
    Print(Ints)
    Print(Strings)
}

上面代码种Print()函数使用泛型,通过在函数名称和参数之间增加[T any]指定泛型变量, 既然T 为any类型,函数可传入任何数据类型slice。当然Print函数不能处理slice之外的输入,这是因为函数实现决定的。通过泛型函数可以避免针对每种数据类型编写函数,这种通用的思想就是泛型。

非泛型函数

我们实现两个函数,返回数值类型数组元素的和。我们至少创建两个函数,一个针对float64类型,另一个为int64,因此工作量翻倍。

package main
import "fmt"
func main() {
   f := []float64{1.0, 2.0, 3.0, 4.0, 5.0}
   i := []int64{1, 2, 3, 4, 5}
   s1 := SumOfFloat(f)
   s2 := SumOfIntegers(i)
   fmt.Println("Sum for float64 :", s1)
   fmt.Println("Sum for int64 :", s2)
}
func SumOfFloat(nums []float64) float64 {
   var sum float64
   for _, num := range nums {
       sum += num
   }
   return sum
}
func SumOfIntegers(nums []int64) int64 {
   var sum int64
   for _, num := range nums {
       sum += num
   }
   return sum
}

运行输出结果:

$ go run main.go
Sum for float64 : 15
Sum for int64 : 15

上面代码针对两种类型定义了两个函数,返回各自参数对应类型数组元素之和。下面通过泛型合并两个函数为一个,节约时间和精力。

泛型函数

本节实现单个函数,参数仍然是数组,但类型可以为float64或int64,并返回数组元素之和,从而可以代替上面两个函数。

为了能够定义函数接收float64和int64两种类型,新的泛型函数需要声明接收的类型,即调用代码需要判断类型是否为float64或int64。为此,新的函数签名需要有点变化,除了普通的参数外,还需要声明类型参数,从而转换函数为泛型函数支持不同类型。

每个形参都有一个类型约束,它指定调用代码可用于形参允许的实参类型。在编译时这些参数表示的类型就是调用代码支持的类型。当然泛型函数的实现逻辑需要支持形参声明的所有类型。举例,泛型函数支持字符串和数值参数,函数实现逻辑需要对参数进行索引,显然数值无法索引将引起编译错误。

泛型函数语法:

func genericFunction[T any](s []T) []T{

}

解释如下:

上面语法种在名称和参数之间的[]用于指定形式参数类型,可以是一组类型或一个约束接口。T 参数类型,用于定义形式参数和返回值类型。

函数内部也可以访问参数,any 是一个interfaceT必须实现该接口。Go1.18版本引入any,底层引用空接口,即interface{}。 下面举例说明:

package main
import "fmt"
func main() {
   f := []float64{1.0, 2.0, 3.0, 4.0, 5.0}
   i := []int64{1, 2, 3, 4, 5}
   s1 := genericSum(f)
   s2 := genericSum(i)
   fmt.Println("Sum for float64 :", s1)
   fmt.Println("Sum for int64 :", s2)
}
func genericSum[N int64 | float64](nums []N) N {
   var sum N
   for _, num := range nums {
       sum += num
   }
   return sum
}

输出结果:

$ go run main.go
Sum for float64 : 15
Sum for int64 : 15

上面示例中演示定义泛型函数genericSum。参数类型在[]中声明的泛型类型。N声明在形参和返回值中使用,N是新的类型,被限定了两种类型float64 和 int64的联合类型。

在函数体类,首先声明N类型变量,然后遍历数组计算综合并返回。

假设泛型函数支持很多种类型,比如:int, int8 , int26, int32, int64, float32, float64。这样函数签名变得非常冗长难看,我们可以定义一个类型包括上述类型,本质上就是把联合类型迁移至函数声明外面。

package main
import "fmt"
type Number interface {
   int64 | float64
}
func main() {
   f := []float64{1.0, 2.0, 3.0, 4.0, 5.0}
   i := []int64{1, 2, 3, 4, 5}
   s1 := genericSum(f)
   s2 := genericSum(i)
   fmt.Println("Sum for float64 :", s1)
   fmt.Println("Sum for int64 :", s2)
}
func genericSum[N Number](nums []N) N {
   var sum N
   for _, num := range nums {
       sum += num
   }
   return sum
}

总结

本文是关于Go泛型的,因为泛型是Golang的新成员。通过对比带泛型和不带泛型的函数,泛型可以很明显地节省时间和精力。泛型不是接口的替代品,两者被设计为一起工作,避免重复代码,让Go类型更安全、代码更整洁。

到此这篇关于Golang泛型的使用方法详解的文章就介绍到这了,更多相关Golang泛型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • GO 使用Webhook 实现github 自动化部署的方法

    GO 使用Webhook 实现github 自动化部署的方法

    这篇文章主要介绍了GO 使用Webhook 实现github 自动化部署的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • Go归并排序算法的实现方法

    Go归并排序算法的实现方法

    归并排序采用的也是分治的策略,把原本的问题先分解成一些小问题进行求解,再把这些小问题各自的答案修整到一起得到原本问题的答案,从而达到分而治之的目的,对Go归并排序算法相关知识感兴趣的朋友一起看看吧
    2022-04-04
  • Go切片的具体使用

    Go切片的具体使用

    本文主要介绍了Go切片的具体使用,包括声明切片、初始化切片、切片的切割、切片的添加、切片的删除、切片的复制、切片的遍历、多维切片等,感兴趣的可以了解一下
    2023-11-11
  • Go 语言结构体链表的基本操作

    Go 语言结构体链表的基本操作

    链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,这篇文章主要介绍了Go 语言结构体链表,需要的朋友可以参考下
    2022-04-04
  • Golang单元测试与覆盖率的实例讲解

    Golang单元测试与覆盖率的实例讲解

    这篇文章主要介绍了Golang单元测试与覆盖率的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • Go语言下载网络图片或文件的方法示例

    Go语言下载网络图片或文件的方法示例

    这篇文章主要介绍了Go语言下载网络图片或文件的方法示例,文中通过示例代码介绍的非常详细,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • Go语言中使用 buffered channel 实现线程安全的 pool

    Go语言中使用 buffered channel 实现线程安全的 pool

    这篇文章主要介绍了Go语言中使用 buffered channel 实现线程安全的 pool,因为Go语言自带的sync.Pool并不是很好用,所以自己实现了一线程安全的 pool,需要的朋友可以参考下
    2014-10-10
  • Golang map与sync.map的异同详解

    Golang map与sync.map的异同详解

    在Go语言中,map和sync.Map都是用于存储键值对的数据结构,但它们在并发安全性、性能和使用场景上存在显著差异,接下来将深入探讨这两种数据结构的异同,感兴趣的朋友可以参考下
    2024-01-01
  • Go语言中的变量和常量

    Go语言中的变量和常量

    这篇文章介绍了Go语言中的变量和常量,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Golang拾遗之自定义类型和方法集详解

    Golang拾遗之自定义类型和方法集详解

    golang拾遗主要是用来记录一些遗忘了的、平时从没注意过的golang相关知识。这篇文章主要整理了一下Golang如何自定义类型和方法集,需要的可以参考一下
    2023-02-02

最新评论