• 泛型初识~go1.18的新类型



    前言

    在之前都未接触过泛型,在之前偶然听别人提及过泛型这东西,所以就学习总结一下go的泛型使用

    一、为什么泛型会在新版的go中加入?


    一个简单的例子来比较用泛型和不用泛型的区别

    • 需求:封装一个函数来实现对多种类型(int、float…)进行加法运算

    由于函数的入参的类型只能定义一个

    func sumInt(a, b int) int {
    	return a + b
    }
    
    • 1
    • 2
    • 3

    所以我们只能使用interface作为入参在利用反射进行类型判断来实现

    func sum_Int_Float(a, b interface{}) interface{} {
      switch a.(type) {
      case int:
        a1 := a.(int)
        b1 := b.(int)
        return a1 + b1
      case float64:
        a1 := a.(float64)
        b1 := b.(float64)
        return a1 + b1
      default:
        return nil
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    但是使用泛型的话那么将是这个样子

    func sum_Int_Float[T int|float64](a,b T) T {
      return a + b
    }
    
    • 1
    • 2
    • 3

    这样看下来使用泛型会简洁很多


    二、泛型语法详解

    一个简单的泛型大概是这样的

    func MyPrintln[T any](a T) {
    	fmt.Println(a)
    }
    
    func main() {
    	MyPrintln("nb")
        //运行结果:
    	//nb
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    MyType[T1 constraint1 | constraint2, T2 constraint3...] ...

    • MyType可以是函数名, 结构体名, 类型名…

    • T1, T2…是泛型名, 可以随便取

    • constraint的意思是约束,是泛型中最重要的概念, T满足其中之一即可(如T1可以是constraint1和constraint2中的任何一个)


    三、constraint约束

    在之前的例子中func MyPrintln[T any](a T)any就是一个约束
    不过看any的底层代码type any = interface{}可知any就跟interface一样


    而go中的约束大概是有这些

    any(interface{}
    Interger
    Float
    comparable (可比较)


    自定义constraint

    然后我们可以看看constraint包里的约束是怎么构造的,然后我们就可以自定义constraint(但是正式版的constraints已经被去除掉了,详细原因

    // Integer is a constraint that permits any integer type.
    // If future releases of Go add new predeclared integer types,
    // this constraint will be modified to include them.
    type Integer interface {
    	Signed | Unsigned
    }
    
    // Float is a constraint that permits any floating-point type.
    // If future releases of Go add new predeclared floating-point types,
    // this constraint will be modified to include them.
    type Float interface {
    	~float32 | ~float64
    }
    //......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    由此我们可知道怎样去自定义约束了

    例如我们想定义一个map,限制它的k,v的类型

    type myCompare interface {
    	~int | ~float64 | [5]interface{} | struct{}
    }
    
    type myint interface {
    	~int8|~int64
    }
    type myfloat interface {
    	~float64|~float32
    }
    
    type MyMap[K myCompare, V myfloat | myint] map[K]V
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样子我们就定义了一个自己的map


    • 除map外,定义泛型结构体变量:
    type Struct1 [T string|int|float64] struct {
      Title string
      Content  T
    }
    
    • 1
    • 2
    • 3
    • 4

    而对于结构体,结构体可以进行匿名操作
    即把结构体的申明定义和初始化一起完成,举个例子

    stu := struct{
      Name string
      Age int
      Weight float64
    }{
      "smallyang",
      18,
      50.5,
    }
    fmt.Println("Student =", stu) // Student = {smallyang 18 50.5}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但是如果对泛型定义的结构体是不支持匿名的

    stu2 := struct[T int|float64] {
      Name   string
      Age    int
      Weight T
    }[int]{
      "smallyang",
      18,
      50,
    }
    fmt.Println("Student =", stu2)
    
    /*
    ./main.go:70:16: syntax error: unexpected [, expecting {
    ./main.go:72:10: syntax error: unexpected int at end of statement
    ./main.go:73:10: syntax error: unexpected T at end of statement
    ./main.go:74:3: syntax error: unexpected [ after top level declaration
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    • 泛型数组变量:
    type slice[T any] []T
    
    • 1

    等等…


    四、泛型中操作各种数据类型的例子示范

    1、操作slice

    package main
    
    import (
    	"fmt"
    )
    
    type slice[T any] []T
    
    type mySlice interface {  自定义constraint
    	~int | ~string
    }
    
    func printSlice[T mySlice](s []T) {
    	for _, v := range s {
    		fmt.Printf("%v ", v)
    	}
    	fmt.Print("\n")
    }
    
    func main() {
    	vs := slice[int]{1, 2, 3, 4}
    	printSlice(vs)
    
    	vs2 := slice[string]{"a", "b"}
    	printSlice(vs2)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2、操作指针

    package main
     
    import (
      "fmt"
    )
     
    func pointerOf[T any](v T) *T {
      return &v
    }
     
    func main() {
      sp := pointerOf("foo")
      fmt.Println(*sp)
     
      ip := pointerOf(123)
      fmt.Println(*ip)
      *ip = 234
      fmt.Println(*ip)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3、操作map

    package main
     
    import (
      "fmt"
    )
     
    func mapFunc[T any, M any](a []T, f func(T) M) []M {
      n := make([]M, len(a), cap(a))
      for i, e := range a {
        n[i] = f(e)
      }
      return n
    }
     
    func main() {
      vi := []int{1, 2, 3, 4, 5, 6}
      vs := mapFunc(vi, func(v int) string {
        return "<" + fmt.Sprint(v*v) + ">"
      })
      fmt.Println(vs)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    c++日期类的实现
    01-win10安装Qt5
    大数据技术生态介绍
    操作系统复习整理
    每天都很煎熬,领导派的活太难,真的想跑路了
    牛客 DP36 abb
    读《vue3设计与实现》2- vue的diff算法核心
    手绘板的制作——手绘(1)
    内网穿透的应用-如何在Docker中部署MinIO服务并结合内网穿透实现公网访问本地管理界面
    链路日志中追踪traceId
  • 原文地址:https://blog.csdn.net/qq_51898139/article/details/126698643