GO泛型

news/2025/3/12 12:39:53/

泛型是goSDK1.18版本之后才引入的新特性,即C++中的模板。

为什么要有泛型?

        我们现在要写一个两数相加的函数,相加的逻辑很简单,但是如果传入不同的类型,那么我们就需要再写一个函数,定义不同的参数类型,才能调用,go语言还没有函数重载,每次还要起不同的函数名,想想就很头疼哇。

        明明逻辑都是一样的,仅仅因为参数类型不同就要重写一份,为了优化这种因素造成的代码冗余,因此我们可以使用泛型去简化代码。👇

package mainfunc main() {a, b := 1, 2c := Add1(a, b)d, e :=1.1, 2.2f :=Add2(d, e)
}func Add1(a, b int) int {return a + b
}func Add2(a, b float64) float64 {return a + b
}func Add3(a, b int64) int64 {}func Add4(a, b int8) int8 {}

所以泛型就是一种不限定参数的类型,让调用的人自己去定义类型的方法。

可能你会想到可以使用接口类型,但是下面我们看一下如果使用接口的话👇

package mainimport "fmt"func main() {a, b := 1, 2c, d := 2.2, 3.3fmt.Println(Add(a, b))fmt.Println(Add(c, d))
}func Add(a, b interface{}) any {if a1, ok := a.(int); ok {if b1, ok := b.(int); ok {return a1 + b1}}if a1, ok := a.(float64); ok {if b1, ok := b.(float64); ok {return a1 + b1}}return nil//if {...}//if {...}//if {...}
}

可以看到,即使使用接口和类型断言之后,还需要加大量的类型断言判断代码,而且一个不注意还容易出错。

1、泛型的使用😁

package mainimport "fmt"func main() {a, b := 1, 2c, d := 1.1, 2.2fmt.Println(Add[int](a, b))fmt.Println(Add[float64](c, d))
}// 这里救使用到了泛型🤗
func Add[T int | float64](a, b T) T {return a + b
}//泛型函数部分有更具体的😁😁😁

2、泛型类型😁

package mainimport "fmt"//使用type定义新类型
type s1 []int
type s2 []float64
type s3 []float32func main() {var a1 s1 = []int{1, 2, 3}var a2 s2 = []float64{1, 2, 3}var a3 s3 = []float32{1, 2, 3}}

         type可以定义新的类型,我们定义三个切片类型,3个切片内类型不同,因此就需要写3行type,定义3个新类型, 我们定义的结构都是一样的,只是它的类型不同,就需要重新定义这么多的类型。

        有了泛型之后我们也可以改良这种代码。👇

package mainimport "fmt"//type s1 []int
//type s2 []float64
//type s3 []float32// 这种类型的定义方式,带了类型形参,和普通定义类型就完全不同的。
// T 的类型可以进行约束,中间使用 |
// 我们定义的类型就是slice[T]
// T 说白了就是一个占位符,类型的形式参数,T是不确定的,需要在使用的时候进行传递。
type slice[T int32 | float64 | float32] []T //🐮🐮🐮func main() {//     传递类型参数🚩var s1 slice[int32] = []int32{1, 2, 3, 4}fmt.Println(s1)var s2 slice[float64] = []float64{1, 2, 3, 4}fmt.Println(s2)var s3 slice[float32] = []float32{1, 2, 3, 4}fmt.Println(s3)//string不在T的约束内,定义的时候就会报错//var s4 slice[string] = slice[string]{"1","2","3","4"}}

更多定义例子

//😊😊😊😊😊
type myMap[KEY int | string, VALUE string] map[KEY]VALUEtype User[ID int, NAME string] struct{id IDname NAME
}type I[T int | string] interface {Hello1(a int)Hello2(a string)Hello3(a T)
}type myChan[T int | string | float64] chan T

 泛型可以定义在一切有类型的地方

3、泛型函数😁

泛型的主要使用就是和函数结合。

方法:

package mainimport ("fmt"
)type MySlice[T int | float32 | int64] []Tfunc main() {var s MySlice[int] = []int{1, 2, 3, 4}fmt.Println(s.sum())var s1 MySlice[float32] = []float32{1.0, 2.0, 3.0, 4.0}fmt.Println(s1.sum())
}//方法使用泛型
func (s MySlice[T]) sum() T {var sum Tfor _, v := range s {sum += v}return sum
}

函数:

package mainimport ("fmt"
)func main() {var a int = 1var b int = 2fmt.Println(Add[int](a, b))var c float32 = 1.1var d float32 = 2.2fmt.Println(Add[float32](c, d))// Go的泛型语法糖:自动推导 (本质,就是编译器帮我们加上去了,在实际运行,这里T还是加上去的)fmt.Println(Add(a, b)) // T : intfmt.Println(Add(c, d)) // T : float32}func Add[T int | float32 | float64](a T, b T) T {return a + b
}

4、自定义泛型😁

package main// 泛型的约束提取定义
type My interface {int|float32|int8|int32 // 作用域泛型的,而不是一个接口方法
}// 自定义泛型
func main()  {var a int = 10var b int = 20GetMaxNum(a,b)
}
func GetMaxNum[T My](a,b T) T {if a>b {return a}  return b
}

5、内置泛型类型😁

any :表示了go所有的内置类型

comparable :表示所有可以比较的类型

6、~(新符号)😁

package mainimport "fmt"// int8 衍生类型
type int8A int8
type int8B = int8// ~ 表示可以匹配该类型的衍生类型
type NewInt interface {~int8
}// ~
func main() {var a int8A = 10var b int8A = 10fmt.Println(GetMax(a, b))
}func GetMax[T NewInt](a, b T) T {if a > b {return a}return b
}

7、泛型的作用😁

        减少重复性的代码,提高安全性,针对不同的类型,写了相同逻辑的代码,我们就可以通过泛型来简化代码!


http://www.ppmy.cn/news/1554385.html

相关文章

2021高等代数【南昌大学】

证明多项式 f ( x ) = 1 + x + x 2 2 ! + ⋯ + x n n ! f(x) = 1 + x + \frac{x^2}{2!} + \cdots + \frac{x^n}{n!} f(x)=1+x+2!x2​+⋯+n!xn​ 无重根。f ( x ) − f ′ ( x ) = x n n ! f(x) - f(x) = \frac{x^n}{n!} f(x)−f′(x)=n!xn​ ( f ( x ) , f ′ ( x ) ) = ( f (…

【Unity高级】如何动态调整物体透明度

本文介绍了如何设置及动态调整物体的透明度。 一、手动设置的方法 我们先来看下如何手动设置物体的透明度。 物体的透明与否是通过材质来设置的。只有我们把具有透明度的材质指给物体的渲染器(Render),物体就被设置成相应的透明度了。 看一…

石头剪子布

石头剪子布 C语言实现C实现Java实现Python实现 💐The Begin💐点点关注,收藏不迷路💐 石头剪子布,是一种猜拳游戏。起源于中国,然后传到日本、朝鲜等地,随着亚欧贸易的不断发展它传到了欧洲&…

三、【docker】docker和docker-compose的常用命令

文章目录 一、docker常用命令1、镜像管理2、容器管理3、容器监控和调试4、网络管理5、数据卷管理6、系统维护7、实用组合命令8、常用技巧二、docker-compose常用命令1、基本命令2、构建相关3、运行维护4、常用组合命令5、实用参数 一、docker常用命令 1、镜像管理 # 查看本地…

力扣--LCR 177.撞色搭配

题目 整数数组 sockets 记录了一个袜子礼盒的颜色分布情况,其中 sockets[i] 表示该袜子的颜色编号。礼盒中除了一款撞色搭配的袜子,每种颜色的袜子均有两只。请设计一个程序,在时间复杂度 O(n),空间复杂度O(1) 内找到这双撞色搭配…

酷柚易汛进销存系统PHP+Uniapp

移动端订货通、商品管理、库存管理、订单管理、客户管理、供应商、财务管理、经营分析 版本更新V1.6.4 1、新增供应商分类不可添加重复类别2、新增客户分类不可添加重复类别3、新增商品分类不可添加重复类别4、新增支出类别不可添加重复类别5、新增收入类别不可添加重复类别6…

21天掌握javaweb-->第13天:Docker容器化部署与微服务简介

Docker基础与Spring Boot应用的容器化部署 Docker的基本概念: Docker是一个开源的应用容器引擎,它使得应用的打包、分发和运行变得更加简单。容器是Docker使用的封装应用及其运行环境的轻量级、可移植的单元。 Docker的核心组件: 镜像&…

【23种设计模式】原型模式:理论剖析与Java实践

文章目录 原型模式:理论剖析与 Java 实践应用一、原型模式概述二、Java 实现示例(一)原型接口(二)具体原型类 三、关键步骤(一)创建原型对象(二)克隆原型对象 四、流程图…