golang泛型简介
linux下go版本安装(1.18.1版本)
>>> wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz
>>> tar xzvf go1.18.1.linux-amd64.tar.gz -C /usr/local
>>> mkdir /data/go_path -p
>>> vim ~/.bashrc
export GOROOT=/usr/local/go
export GOPATH=/data/go_path
export GOPROXY=https://goproxy.cn
export PATH=$PATH:$GOROOT/bin::$GOPATH/bin
>>> source ~/.bashrc
编写泛型函数
让我们看看如何在go中构建自己的泛型函数开始。传统上,你将从一个函数签名开始,该签名将明确说明此函数期望作为参数的类型:
func oldNonGenricFunc(myAge int64){
fmt.Println(myAge)
}
在新版本中,如果我们想创建一个可以接收int64
和float64
类型的函数,我们可以像这样修改我们的函数签名:
package main
import "fmt"
func newGenericFunc[age int64 | float64](myAge age){
fmt.Println(myAge)
}
func main(){
fmt.Println("Go Generics Tutorial")
var testAge int64 = 23
var testAge2 float64 = 24.5
newGenericFunc(testAge)
newGenericFunc(testAge2)
}
运行结果:
[root@wei demo_generics]# go run main.go
go generics
23
24.5
所以, 让我们分解一下我们在这里所做的时区。我们有效的创建了一个名为newGenericFunc
函数,在我们的函数名称之后,我们打开方括号[]
并指定我们可以合理地期望我们的函数被调用的类型:
[age int64 | float64]
当我们在括号中定义函数的参数时,我们说变量myAge
可以是类型age
, 随后可以是int64
或者float64
类型。
注意,如果我们想添加更多类型,我们可以使用
|
不同类型之间的分隔符列出更多类型。
使用任何类型
在上面的列子中,我们指定了一个可以接受多种类型的泛型函数int64
,float64
,但是,如果我们想要定义一个可以接受任何类型的函数怎么办?
我们可以像下面这样使用新的内置any
类型:
package main
import "fmt"
func newGenericFunc[age ant](myAge age){
fmt.Println(myAge)
}
func main(){
fmt.Println("Go Generics Tutorial")
var testAge int64 = 23
var testAge2 float64 = 24.5
var testString string = "Elliot"
newGenericFunc(testAge)
newGenericFunc(testAge2)
newGenericFunc(testString)
}
运行结果:
[root@wei demo_generics]# go run main.go
go generics
23
24.5
Elliot
让我们看一个可能引发问题的案例。让我们更新我们的代码以对myAge
参数进行一些额外的计算。我们将尝试将其转换为int
然后将值加1:
package main
import "fmt"
func newGenericFunc[age any](myAge age){
val := int(myAge) + 1
fmt.Println(val)
}
func main(){
fmt.Println("Go Generics Tutorial")
var testAge int64 = 23
var testAge2 float64 = 24.5
var testString string = "Elliot"
newGenericFunc(testAge)
newGenericFunc(testAge2)
newGenericFunc(testString)
}
现在,当我们尝试构建或运行这段代码时,我们应该看到它编译失败:
>>> go run main.go
./generic_issue.go:6:13: cannot convert myAge (variable of type age constrained by any) to type int
在这种情况下,我们无法尝试将类型转换为any
to int
。解决这个问题的唯一方法是更明确地传递传入的类型,如下所示:
package main
import "fmt"
func newGenericFunc[age int64 | float64](myAge age) {
val := int(myAge) + 1
fmt.Println(val)
}
func main() {
fmt.Println("Go Generics Tutorial")
var testAge int64 = 23
var testAge2 float64 = 24.5
newGenericFunc(testAge)
newGenericFunc(testAge2)
}
在大多数情况下,在我们可以合理使用的类型中更加明确是有利的,因为它可以让您对如何处理每个单独的类型进行最深思熟虑和深思熟虑。
显式传递类型参数
在大多数情况下,Go 将能够推断您传递给泛型函数的参数的类型。但是,在某些情况下,您可能希望更加慎重并指定要传递给这些通用函数的参数的类型。
了更明确,我们可以使用相同的[]
括号语法来说明正在传递的参数的类型:
newGenericFunc[int64](testAge)
这将明确指出,当传入这个newGenericFunc
时,testAge变量将是int64
类型。
类型约束
让我们看看如何在 Go 中修改代码并声明类型约束。
在这个例子中,我们将把我们的泛型函数可以接受的类型移动到我们标记的接口中Age
。然后函数newGenericFunc
像这样使用了这个新的类型约束:
package main
import "fmt"
type Age interface{
int64 | int32 | float32 | float64
}
func newGenericFunc[age Age](myAge age){
val := int(myAge) + 1
fmt.Println(val)
}
func main(){
fmt.Println("Go Generics Tutorial")
var testAge int64 = 23
var testAge2 float64 = 24.5
newGenericFunc(testAge)
newGenericFunc(testAge2)
}
运行结果:
[root@wei demo_generics]# go run main.go
go generics
24
25
现在,这种方法的美妙之处在于,我们可以在整个代码中重用这些相同的类型约束,就像我们在 Go 中使用任何其他类型一样。
更复杂的类型约束
让我们看一个稍微复杂一点的用例。例如,假设我们想要创建一个getSalary
函数,该函数将接收满足给定类型约束的任何内容。我们可以通过定义一个接口然后将其用作泛型函数的类型约束来实现这一点:
package main
import "fmt"
type Employee interface{
PrintSalary()
}
func getSalary[E Employee](e E){
e.PrintSalary()
}
type Engineer struct {
Salary int32
}
func (e Engineer)PrintSalary(){
fmt.Println(e.Salary)
}
type Manager struct{
Salary int64
}
func (m Manager)PrintSalary(){
fmt.Println(m.Salary)
}
func main(){
fmt.Println("Go Generics Tutorial")
engineer := Engineer{Salary: 10}
manager := Manager{Salary: 100}
getSalary(engineer)
getSalary(manager)
}
在这个例子中,我们已经指定我们的getSalary
函数有一个类型约束,E
它必须实现我们的Employee
接口。在我们的主函数中,我们定义了一个工程师和一个经理,并将这两个不同的结构传递到getSalary
函数中,即使它们都是不同的类型。
>>> go run main.go
Go Generics Tutorial
10
100
现在这是一个有趣的例子,它展示了我们如何对泛型函数进行类型约束以仅接受实现此PrintSalary
接口的类型,然而,同样可以通过直接在函数签名中使用接口来实现,如下所示
func getSalary(e Employee) {
e.PrintSalary()
}
是相似的,因为在 Go 中使用接口是一种泛型编程,理解方法之间的差异和一种方法的好处可能在官方 go.dev 题为“为什么泛型”的帖子中得到更好的解释.
泛型的好处
到目前为止,我们刚刚介绍了在 Go 中编写通用代码时会遇到的基本语法。让我们将这些新发现的知识更进一步,看看这段代码在我们自己的 Go 应用程序中的哪些方面是有益的。
让我们看一个标准的 BubbleSort 实现:
func BubbleSort(input []int) []int{
n := len(input)
swapped := true
for swapped {
// 设置swapped为false
swapped = false
for i := 0;i < n-1; i++{
if input[i] > input[i+1]{
fmt.Println("Swapping")
input[i], input[i+1] = input[i+1], input[i]
swapped = true
}
}
}
现在,在上面的实现中,我们已经定义了这个 BubbleSort 函数必须接受一个类型int
切片。例如,如果我们试图用一个类型的切片运行它int32
,我们会得到一个编译器错误:
cannot use list (variable of type []int32) as type []int in argument to BubbleSort
让我们看看我们如何使用泛型编写它并打开输入以接受所有 int 类型和 float 类型:
package main
import "fmt"
type Number interface{
int16 | int32 | int64 | float32 | float64
}
func BubbleSort[N Number](input []N)[]N{
n := len(input)
swapped := true
for swapped{
swapped = false
for i := 0;i<n-1;i++{
if input[i] > input[i+1]{
input[i], input[i+1] = input[i+1], input[i]
swapped = true
}
}
}
return input
}
func main(){
fmt.Println("Go Generics Tutorial")
list := []int32{4,3,1,5,}
list2 := []float64{4.3, 5.2, 10.5, 1.2, 3.2,}
sorted := BubbleSortGeneric(list)
fmt.Println(sorted)
sortedFloats := BubbleSortGeneric(list2)
fmt.Println(sortedFloats)
}
通过对我们的BubbleSort函数进行这些修改,并接受一个类型受限的Number,我们已经有效地使自己能够减少我们必须写的代码量,如果我们想支持每一个int和float类型的话!
让我们现在尝试运行它:
Go Generics Tutorial
[1 3 4 5]
[1.2 3.2 4.3 5.2 10.5]
官网泛型教程
https://go.dev/doc/tutorial/generics
golang泛型简介的更多相关文章
- Golang 泛型的简单使用
go 学习泛型,利用泛型编写对数据集合执行操作的方法.
- C# 泛型简介
摘要:本文讨论泛型处理的问题空间.它们的实现方式.该编程模型的好处,以及独特的创新(例如,约束.一般方法和委托以及一般继承).此外,本文还讨论 .NET Framework 如何利用泛型. 下载 Ge ...
- Java基础之集合框架类及泛型简介
Collection接口 Collection 通用的常见方法 add()添加一个元素,可以指定脚标 addAll()将一个collection放入 clear()清除 remove()删除元素,返回 ...
- Golang 并发简介
并发概要 随着多核CPU的普及, 为了更快的处理任务, 出现了各种并发编程的模型, 主要有以下几种: 模型名称 优点 缺点 多进程 简单, 隔离性好, 进程间几乎无影响 开销最大 多线程 目前使用最多 ...
- Golang 特性简介
by sheepbao 主要大概介绍go语言的历史和特性,简单的入门. 来历 很久以前,有一个IT公司,这公司有个传统,允许员工拥有20%自由时间来开发实验性项目.在2007的某一天,公司的几个大牛, ...
- Java学习:泛型简介
泛型 泛型:是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型 泛型也可以看出是一个变量,用来接受数据类型 E e : Element 元素 T t : Type 类型 Array ...
- java 泛型简介(转载)
原文出处: absfree 1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过 ...
- C# 泛型约束
一.泛型简介1.1泛型通过使用泛型,可以创建这样的类.接口和方法,它们以一种类型安全的工作方式操作各种数据.本质上,术语“泛型”指的是“参数化类型”(parameterized types).参数化类 ...
- C# 集合与泛型
一.古典集合方式 在C#2.0的时候集合主要通过两种方式实现: 1.使用ArrayList实现 新建ArrayList,然后将所有对象放入该数组中,简单直接,但缺点是该数组什么类型的元素都能接收,在实 ...
- Java笔记--泛型总结与详解
泛型简介: 在泛型没有出来之前,编写存储对象的数据结构是很不方便的.如果要针对每类型的对象写一个数据结构, 则当需要将其应用到其他对象上时,还需要重写这个数据结构.如果使用了Object类型, ...
随机推荐
- 【转帖】网卡bonding模式 - bond0、1、4配置
网卡bonding简介 网卡绑定就是把多张物理网卡通过软件虚拟成一个虚拟的网卡,配置完毕后,所有的物理网卡的ip和mac将会变成相同的.多网卡同时工作可以提高网络速度,还可以实现网卡的负载均衡.冗余. ...
- [转帖] 常见的Socket网络异常场景分析
https://www.cnblogs.com/codelogs/p/16001770.html 原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介# 在目前微服务的 ...
- [转帖]nacos开启强鉴权
注意 Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险. Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系. 如果 ...
- Oracle 高版本导出到低版本的测试验证
今天验证Oracle 由高版本 备份恢复到低版本 与方神沟通(双 还是他) 说可以使用 version的参数..然后搞一下.. expdp system/Test6530@ora12cr2 schem ...
- Linux查找当前目录下包含部分内容的文件,并且copy到指定路径的简单方法
1 获取文件列表 find . -name "*.data" |xargs grep -i 'yearvariable' | uniq | awk '{print $1}' |cu ...
- locust+python性能测试库
一.简介 locust官网介绍:Locust 是一个用于 HTTP 和其他协议的开源性能/负载测试工具.其对开发人员友好的方法允许您在常规 Python 代码中定义测试.Locust测试可以从命令行运 ...
- 模块化Common.js与ES6
为什么要模块化开发 1. 依赖关系(a文件依赖b文件中的方法,b文件必须在a文件之前引入) 2. 命名问题 (多个文件变量名,方法名相同会出现覆盖) 3. 代码组织(后期不好维护) 模块化规范有 1. ...
- 【K哥爬虫普法】大数据风控第一案:从魔蝎科技案件判决,看爬虫技术刑事边界
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...
- Fabric网络升级(总)
原文地址在这里. 在fabric网络中,升级nodes和通道至最新版本需要四步: 备份账本和MSPs. 以滚动的方式将orderer升级到最新版. 以滚动的方式将peers升级到最新版. 将order ...
- 从零开始配置vim(21)——会话管理
很多代码编辑器都有这么一个功能,重新进入编辑器之后能恢复上次打开的所有文件,窗口布局,有的甚至是上次设置的一些配置.那么vim是否也可以实现这样的功能呢?答案是肯定的.使用vim自带的会话管理和 vi ...