前言

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

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


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

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

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

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

所以我们只能使用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
  }
}

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

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

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


二、泛型语法详解

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

func MyPrintln[T any](a T) {
	fmt.Println(a)
}

func main() {
	MyPrintln("nb")
    //运行结果:
	//nb
}

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
}
//......

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

例如我们想定义一个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

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


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

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

stu := struct{
  Name string
  Age int
  Weight float64
}{
  "smallyang",
  18,
  50.5,
}
fmt.Println("Student =", stu) // Student = {smallyang 18 50.5}

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

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
*/

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

等等…


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

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)
}

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)
}

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)
}