golang 1.18泛型

泛型(1.18新特性)

泛型的概念理解

Go语言他是在进化的,不断的迭代,优化功能!

1.0 - 1.19 (1.20)/ 和最开始Go语言比起来发生了很多变化。

1.7 Context

1.11 modules

1.13 error嵌套

1.18 泛型(类型参数)

https://segmentfault.com/a/1190000041634906

泛型的出现,很受期待,但是很少用!场景不多。

打印一个切片(传递不同的参数string 、int)

package main

import (
   "fmt"
)

func main() {
   is := []int{1, 2}
   //strs := []string{"kuangshen", "xuexiangban"}
   printSlice(is)
   //printSlice(strs)
}

// 如何实现一个方法可以打印上面不同类型的切片呢?  反射
func printSlice(s interface{}) {
   // 断言 x.(T), 如果x实现了T,那么就将 x转换为T类型
   for _, v := range s.([]string) {
      fmt.Println(v)
   }
}

这种情况,传递的参数进来,需要自己做N种判断来进行适配。

泛型

思考:不限定参数的类型,让调用的人自己去定义类型。

// 参数的类型是不确定的,让用户自己指定。
// 泛型也是使用 [],和数组很像!
// 
func printSlice[T any](s []T) {
	for _, v := range s {
		fmt.Println(v)
	}
}

printSlice , [T any] 泛型的约束!由于我们不确定这个函数的参数类型。我们希望用户给我们传递这个值。

print[T string](name T)
print[T string|int](name T)


func Min[T int|int8|int16|int32|int64](a,b T){

}

泛型的作用:

1、减少重复性的代码,提高安全性

  • 针对不同的类型,写了相同逻辑的代码,我们就可以通过泛型来简化代码!

2、在1.18版本之前 反射 来实现。 泛型并不能完全取代反射!

泛型:多的多个类型(类型可以是不确定的)

var 变量名 数据类型 = 值
var 变量名 T = 值 // 泛型的逻辑

泛型类型

package main

import "fmt"

// s1 是我们自己定义的类型 ,本质 []int
type s1 []int
type s2 []float64
type s3 []float32

// 我们定义的结构都是一样的,只是它的类型不同,就需要重新定义这么多的类型。
// 思考:是否有一种机制,只定义一个类型就可以代表上面的所有类型?
// 泛型:类型 参数化了!   参数:人为传递的

/*
1、T 说白了就是一个占位符,类型的形式参数,T是不确定的,需要在使用的时候进行传递。
2、由于T类型是不确定的,我们需要加一些约束 int|float64|float32 。告诉编译器我这个T,只接受
  int、float64、float32 类型
3、我们这里定义的类型是什么?Slice[T]
*/

// 这种类型的定义方式,带了类型形参,和普通定义类型就完全不同的。
// 普通的定义类型,这个类型只能代表本身一个,泛型类型,我们可以实现,参数类型传递。
// 我们可以在使用的时候来定义类型。
// 语法糖:简化开发
type Slice[T int | float64 | float32] []T

func main() {

	var a Slice[int] = []int{1, 2, 3}
	fmt.Printf("%T\n", a) // Slice[int]

	var b Slice[float64] = []float64{1.0, 2.0, 3.0}
	fmt.Printf("%T\n", b) // Slice[float64]
	
	// 不能够赋值(string 不在T的约束当中,不能实例化的)
	//var c Slice[string] = []string{"kuangshen","xxx"}
	
	// T是占位符,在使用的时候,必须要实例化为具体的类型。
	//var d Slice[T] = []int{1,2,3}
}

func test(name interface{}) {
	fmt.Println(name)
}

泛型的类型使用

package main

import "fmt"

// 泛型可以用在所有有类型的地方

type MyStruct[T int | string] struct {
   Id   T
   Name string
}

type IprintData[T int|float64|string] interface {
   Print(data T)
}

// 通道
type MyChan[T int|string|float64] chan T

func main() {

   //T 泛型的参数类型的属性可以远不止一个,所有东西都可以泛型化。
   // map(int)string

   // map[KEY]VALUE 类型形参(参数是不确定) KEY  、VALUE
   // KEY int | string   , VALUE float32 | float64 约束
   // 类型的名字 MyMap[KEY,VALUE], 通过这一个类型,来代表多个类型!--> 泛型

   type MyMap[KEY int | string | float64, VALUE float32 | float64 | int] map[KEY]VALUE

   // map [string]float64
   var score MyMap[string, float64] = map[string]float64{
      "go":   9.9,
      "java": 8.0,
   }
   fmt.Println(score)
}

特殊的泛型

package main

func main()  {
   // 特殊的泛型类型,泛型的参数时多样的,但是实际类型定义就是int
   type AAA[T int|string] int
   
   var a AAA[int] = 123
   var b AAA[string] = 123
   //var c AAA[string] = "hello" // 底层类型是int
}

这里虽然使用了泛型。但是底层类型就是int,所以无论传什么都可以的,但是赋值,只能是int、

这个例子没什么意义。

泛型函数

单纯的泛型没啥意义。和函数结合使用, 可以使用调用者(调用者的类型可以自定义,就可以实现泛型。)

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.4}
	fmt.Println(s1.sum())
}

// 调用者,类型是不确定的,用户传什么,她就实例化什么。  类型参数化了 , 泛型
// 没有泛型之前, 反射: 	reflect.ValueOf().Kind() , 也需要很多if,本质是逻辑相同的,只是类型不同!
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))

   // 每次都去写T的类型是很麻烦的,支持自动推导!
   // Go的泛型语法糖:自动推导 (本质,就是编译器帮我们加上去了,在实际运行,这里T还是加上去的)
   fmt.Println(Add(a, b)) // T : int
   fmt.Println(Add(c, d)) // T : float32

}

// 真正的Add实现,传递不同的参数都是可以适配的! Add[T]  T在调用的时候需要实例化
// 这种带了类型形参的函数就叫做泛型函数,极大的提高代码的灵活心,降低阅读性!
func Add[T int | float32 | float64](a T, b T) T {
   return a + b
}

1、泛型类型 (自定义类型结合泛型使用)

2、泛型作为接收者 (实现函数的灵活变化)

3、泛型函数(参数是泛型)

计算机底层的运行,绕不过的,你只能简化代码开发,不能简化实际运行操作!

自定义泛型

由于约束有时候很多,我们可以定义一些自己的泛型约束(本质是一个接口)

package main

// 泛型的约束提取定义
type MyInt interface {
   int|float32|int8|int32 // 作用域泛型的,而不是一个接口方法
}

// 自定义泛型
func main()  {
   var a int = 10
   var b int = 20

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

内置泛型类型:

any (就是一个泛型,表示了go所有的内置类型。interface{})

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

新符号 ~,和类型一起出现的,表示支持该类型的衍生类型!

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
}

总结泛型:

1、类型参数 (T)

2、类型集合 (T,K )map slice

3、类型推断 (简化开发)

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sanyueshui_Go

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值