Files
notes/resource/go/待总结/014 Go 语言泛型和网络编程.md
T
2026-03-01 01:43:46 +08:00

342 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 泛型(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)
```go
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种判断来进行适配。
> 泛型
思考:不限定参数的类型,让调用的人自己去定义类型。
```go
// 参数的类型是不确定的,让用户自己指定。
// 泛型也是使用 [],和数组很像!
//
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 = 值 // 泛型的逻辑
```
## 泛型类型
```go
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)
}
```
泛型的类型使用
```go
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)
}
```
> 特殊的泛型
```go
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、
这个例子没什么意义。
## 泛型函数
单纯的泛型没啥意义。和函数结合使用, 可以使用调用者(调用者的类型可以自定义,就可以实现泛型。)
```go
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
}
```
泛型可以增加代码的灵活性,降低了可读性!
```go
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、泛型函数(参数是泛型)
计算机底层的运行,绕不过的,你只能简化代码开发,不能简化实际运行操作!
## 自定义泛型
由于约束有时候很多,我们可以定义一些自己的泛型约束(本质是一个接口)
```go
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 :表示所有可以比较的类型
新符号 ~,和类型一起出现的,表示支持该类型的衍生类型!
```go
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、类型推断 (简化开发)