golang-泛型基础篇(一)

本文简单介绍go泛型的概念和使用。

函数重载

func Add(a int, b int) int {
    return a + b
}

这个函数很简单,但是它无法计算int类型之外的和。如果我们想计算浮点或者字符串的和该怎么办?解决方法就是对它进行方法的重载。但是golang不支持对方法进行重载。

//错误
func AddFloat32(a float32, b float32) float32 {
    return a + b
}

func AddString(a float64, b float64) float64{
    return a + b
}

可以通过泛型来解决。

func Add[T int | float64](a T, b T) T {
	return a + b
}

泛型方法:该方法在调用时可以接收不同类型的参数。一个函数获得了处理多种不同类型数据的能力,这种编程方式被称为 泛型编程
泛型有着自己的适用场景: 如果你经常要分别为不同的类型写完全相同逻辑的代码,那么使用泛型将是最合适的选择。

Go的泛型

之前我们定义一个可以容纳 int 或 float32或 int32 等其他类型的切片。

type intSlice []int 
type Float32Slie []float32
type int32Slice []int32 

现在我们可以这样

//不同于一般的类型定义,这里类型名称 SliceT 后带了中括号
type SliceT[T int | float32 | int32 ] []T
  • 类型形参: T 在定义SliceT类型的时候 T 代表的具体类型并不确定,类似一个占位符。
  • 类型约束: int | float32 | int32 中间的 | 的意思是告诉编译器,类型形参 T 只可以接收 int 或 float32 或 float64 这三种类型的实参。
  • 类型形参列表: T int | float32 | int32
  • 这里新定义的类型名称叫 SliceT[T]

泛型类型:类型定义中带 类型形参 的类型

泛型实例

切片泛型定义

type SliceT[T int | float32 | int32 | string] []T
func SliceFunc() {
	// 这里传入了类型实参int,泛型类型SliceT[T]被实例化为具体的类型 Slice[int]
	var a SliceT[int] = []int{1, 2, 3}
	fmt.Printf("Type Name: %T\n", a) //输出:Type Name: Slice[int]

	// 传入类型实参float32, 将泛型类型SliceT[T]实例化为具体的类型 Slice[string]
	var b SliceT[float32] = []float32{1.0, 2.0, 3.0}
	fmt.Printf("Type Name: %T\n", b) //输出:Type Name: Slice[float32]

	// ✗ 错误。string不在类型约束 int|float32|float64 中,不能用来实例化泛型类型
	//var c SliceT[string] = []string{"Hello", "World"}
}

map泛型定义

type MyMap[KEY int | string, VALUE float32 | float64] map[KEY]VALUE
func MapFunc() {
	var mp MyMap[string, float64] = map[string]float64{
		"jack_score": 9.6,
		"bob_score":  8.4,
	}
	fmt.Println(mp)
}

channel泛型定义

type MyChan[T int | string] chan T
func ChanFunc() {
	ch := make(MyChan[int], 3)
	for i := 1; i <= 3; i++ {
		ch <- i
	}
	fmt.Println(<-ch)
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

接口泛型定义

// 一个泛型接口
type IPrintData[T int | float32 | string] interface {
    Print(data T)
}

简单结构体泛型定义

// 一个泛型类型的结构体。可用 int 或 sring 类型实例化
type MyStruct[T int | string] struct {
	Name string
	Data T
}
type MyStruct2[T int | string, A int | bool] struct {
	Name string
	Data T
	Sex  A
}

func StructFunc() {
	var my MyStruct[int] = MyStruct[int]{
		Name: "caicai",
		Data: 23,
	}
	fmt.Println(my)

	var my2 MyStruct2[int, bool] = MyStruct2[int, bool]{
		Name: "caicai",
		Data: 23,
		Sex:  true,
	}
	fmt.Println(my2)
}

类型形参的互相套用

type SliceT[T int | float32 | int32 | string] []T
type SliceT2[T int | float32 | int32] []T
type MyStruct3[T int | string] struct {
	Name  string
	Data  T
	habby SliceT[T]
	//habby2 SliceT2[T]
}
func Struct2Func() {
	var my MyStruct3[string] = MyStruct3[string]{
		Name:  "caicai",
		Data:  "23",
		habby: []string{"swim"},
	}

	fmt.Println(my)
}

我们知道 MyStruct3的约束是 int或 string 当传入的 string 时,属性habby2 不满足string,报错。

几种语法错误

  • 定义泛型类型的时候,基础类型不能只有类型形参,如下:
// 错误,类型形参不能单独使用
type CommonType[T int|string|float32] T
  • 当类型约束的一些写法会被编译器误认为是表达式时会报错
//✗ 错误。T *int会被编译器误认为是表达式 T乘以int,而不是int指针
type NewType[T *int] []T
// 上面代码再编译器眼中:它认为你要定义一个存放切片的数组,数组长度由 T 乘以 int 计算得到
type NewType [T * int][]T 

//✗ 错误。和上面一样,这里不光*被会认为是乘号,| 还会被认为是按位或操作
type NewType2[T *int|*float64] []T 

//✗ 错误
type NewType2 [T (int)] []T 
- 为了避免这种误解,解决办法就是给类型约束包上 interface{} 或加上逗号消除歧义
type NewType[T interface{*int}] []T
type NewType2[T interface{*int|*float64}] []T 

// 如果类型约束中只有一个类型,可以添加个逗号消除歧义
type NewType3[T *int,] []T

//✗ 错误。如果类型约束不止一个类型,加逗号是不行的
type NewType4[T *int|*float32,] []T 
因为上面逗号的用法限制比较大,这里推荐统一用 interface{} 解决问题

泛型类型的套娃

// 先定义个泛型类型 Slice[T]
type Slice[T int | string | float32 | float64] []T

// ✗ 错误。泛型类型Slice[T]的类型约束中不包含uint, uint8
//type UintSlice[T uint | uint8] Slice[T]

// ✓ 正确。基于泛型类型Slice[T]定义了新的泛型类型 FloatSlice[T] 。FloatSlice[T]只接受float32和float64两种类型
type FloatSlice[T float32 | float64] Slice[T]

// 在map中套一个泛型类型Slice[T]
type WowMap[T int | string] map[string]Slice[T]

小结

  • 为了实现泛型,Go引入了一些新的概念:
    • 类型形参
    • 类型形参列表
    • 类型约束
    • 实例化 - 泛型类型不能直接使用,要使用的话必须传入类型实参进行实例化。
  • 匿名结构体不支持泛型;
  • 泛型类型的定义;
  • 泛型类型的嵌套使用。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang自V1.18版本开始正式支持泛型泛型Golang中可用于变量、函数、方法、接口、通道等多个语法实体。泛型的使用方法可以分为以下几个方面: 1. 泛型变量类型:泛型变量类型是针对类型变量的,可以用于声明包含不同类型的变量。例如,可以声明一个泛型切片变量,使其可以存储不同类型的元素。 2. 自定义泛型类型:在Golang中,可以通过声明自定义的泛型类型来扩展泛型的使用。这意味着可以定义适用于不同类型的自定义数据结构。 3. 调用带泛型的函数:通过使用泛型类型作为函数参数或返回值,可以调用带有泛型的函数。这样可以实现在不同类型上通用的函数逻辑。 4. 泛型结构体泛型可以与结构体一同使用,结构体中的字段和方法可以是泛型类型,从而实现更灵活和通用的数据结构和操作。 然而,需要注意的是,Golang泛型还存在一些限制和缺陷。例如,无法直接与switch语句配合使用。这是因为在泛型中无法判断具体的类型,而switch语句需要根据具体类型进行分支判断。 总的来说,Golang泛型提供了一种通用的类型机制,使得代码更具灵活性和可复用性。但是需要熟悉泛型的语法和使用限制,以避免在实际使用中遇到问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [全面解读!Golang泛型的使用](https://blog.csdn.net/QcloudCommunity/article/details/125401490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [go泛型使用方法](https://blog.csdn.net/qq_42062052/article/details/123840525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值