GO泛型

发布于:2024-12-18 ⋅ 阅读:(81) ⋅ 点赞:(0)

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

为什么要有泛型?

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

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

package main


func main() {
	a, b := 1, 2
	c := Add1(a, b)
	
	d, e :=1.1, 2.2
	f :=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 main

import "fmt"

func main() {
	a, b := 1, 2

	c, d := 2.2, 3.3

	fmt.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 main

import "fmt"

func main() {
	a, b := 1, 2
	c, d := 1.1, 2.2
	fmt.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 main

import "fmt"

//使用type定义新类型
type s1 []int
type s2 []float64
type s3 []float32


func 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 main

import "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]VALUE

type User[ID int, NAME string] struct{
	id ID
	name 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 main

import (
	"fmt"
)

type MySlice[T int | float32 | int64] []T

func 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 T
	for _, v := range s {
		sum += v
	}
	return sum
}

函数:

package main

import (
	"fmt"
)

func main() {
	var a int = 1
	var b int = 2
	fmt.Println(Add[int](a, b))

	var c float32 = 1.1
	var d float32 = 2.2
	fmt.Println(Add[float32](c, d))
	
	// Go的泛型语法糖:自动推导 (本质,就是编译器帮我们加上去了,在实际运行,这里T还是加上去的)
	fmt.Println(Add(a, b)) // T : int
	fmt.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 = 10
   var b int = 20

   GetMaxNum(a,b)
}
func GetMaxNum[T My](a,b T) T {
   if a>b {
      return a
   }  
   return b
}

5、内置泛型类型😁

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

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

6、~(新符号)😁

package main

import "fmt"

// int8 衍生类型
type int8A int8
type int8B = int8


// ~ 表示可以匹配该类型的衍生类型
type NewInt interface {
	~int8
}

// ~
func main() {
	var a int8A = 10
	var b int8A = 10
	fmt.Println(GetMax(a, b))
}

func GetMax[T NewInt](a, b T) T {
	if a > b {
		return a
	}
	return b
}

7、泛型的作用😁

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


网站公告

今日签到

点亮在社区的每一天
去签到