一、基础知识

1.1 形参和实参

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

func main() {
    minNum := min(100, 200)
}

如上ab叫形参(parameter),100200叫实参(argument)。

1.2 类型形参、类型实参、类型约束、类型形参列表

func sumNum[T int32 | float32](n []T) T {
    var s T
    for _, item := range n {
        s += item
    }
    return s
}

func main() {
    data1 := []int32{10, 20, 30, 40, 50}
    data2 := []float32{10.1, 20.2, 30.3, 40.4, 50.5}
    sum1 := sumNum[int32](data1)
    sum2 := sumNum(data2)
    fmt.Printf("sum1: %v (%T)\n", sum1, sum1)
    fmt.Printf("sum2: %v (%T)\n", sum2, sum2)
}

type customMap[K string | int, V string | int32 | float32] map[K]V
  • 上面sumNum函数中的T,就是类型形参(type parameter)
  • sum1 := sumNum[int32](data1)中的int32就是类型实参(type argument
  • int32 | float32 叫类型约束(type constraint)
  • T int32 | float32叫类型形参列表(type parameter list
  • customMap叫泛型类型(Generic type)
  • sumNum[int32](data1)传入类型实参(int32)确定具体类型的操作叫实例化(instantiations)

1.3 方法集和类型集

Go 1.18 之前非空的interface都是方法集(Method Set),如下ReadWriter就是一个方法集:

type ReadWriter interface {
    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

为了支持泛型,引入了类型集(Type Set)的概念,如下Float是一个类型集(可以表示为float32或者float64),| 表示是多个类型的并集的概念:

type Float interface {
    float32 | float64
    MethodA()
}

type Slice[T Float] []T 

~float32可以表示所有底层是float32类型的数据,比如下面Float可以表示为MyFloat32类型:

type MyFloat32 float32

type Float interface {
    ~float32 | ~float64
    MethodA()
}

没有|分隔符,这种表示交集:

type AllInt interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint32
}

type Uint interface {
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

type A interface { // 接口A代表的类型集是 AllInt 和 Uint 的交集
    AllInt
    Uint
}

type B interface { // 接口B代表的类型集是 AllInt 和 ~int 的交集
    AllInt
    ~int
}

1.4 为什么不能像C++或者Java那样用表示泛型类型

a, b = w < x, y > (z)

如果用<T>来表示泛型类型,上面这种场景编译器不知道是应该解释为:

a = w < x 
b = y > (z)

还是应该解释为这种:

a = (w<x, y>)
b = (z)    

所以最后改成了[T]的形式。这样的话,跟大于号、小于号也能区分开,编译器解析的时候也能更容易。

Why not use the syntax F like C++ and Java?

1.5 基本接口和一般接口

接口中只有方法的叫基本接口(Basic Interface),接口内有类型的叫一般接口(General Interface

type MyError interface { // 接口中只有方法,所以是基本接口
    Error() string
}

type Uint interface { // 接口 Uint 中有类型,所以是一般接口
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}

type ReadWriter interface {  // ReadWriter 接口既有方法也有类型,所以是一般接口
    ~string | ~[]rune

    Read(p []byte) (n int, err error)
    Write(p []byte) (n int, err error)
}

一般接口类型不能用来定义变量,只能用于泛型的类型约束中。

1.6 comparable 约束

go新增了一个 comparable interface 作为约束。comparable只支持==!=操作。><是不支持的,官方在实验库里面提供了一个Ordered

Go的类型能不能比较,是通过 t.equal != nil 来判断的,如果实现了equal函数,就表示当前类型是可以比较的。

func (t *rtype) Comparable() bool {
    return t.equal != nil
}

可以比较的类型

  • boolean
  • numeric
  • string,
  • pointer
  • channel
  • interface types
  • structs – if all it’s field type is comparable
  • array – if the type of value of array element is comparable

不可比较的

  • Slice
  • Map
  • Function

二、Go 泛型使用方式

2.1 基础语法

// ok 
type Map[K string | int, V string | int32 | float32] map[K]V
// ok
type Slice[T int | float32 | float64] []T
// 编译器报错,类型形参T不能单独使用
type CustomType[T int | string | float32] T

// ok, 指针类型
type NewType2[T interface{*int|*float64}] []T 
// 编译器报错
type NewType2[T *int|*float64] []T 


type Slice[T int|string|float32|float64] []T
type String2Array[T int|string] map[string]Slice[T]

2.2 泛型func

func sumNum[T int32 | float32](n []T) T {
    var s T
    for _, item := range n {
        s += item
    }
    return s
}

type Number interface {
    int | int8 | int16 | int32 | int64 | float32
}

func sumNum2[T Number](nums []T) T {
    var s T
    for _, item := range nums {
        s += item
    }
    return s
}

func main() {
    data1 := []int32{10, 20, 30, 40, 50}
    data2 := []float32{10.1, 20.2, 30.3, 40.4, 50.5}
    sum1 := sumNum[int32](data1)
    sum2 := sumNum2(data2)
    fmt.Printf("sum1: %v (%T)\n", sum1, sum1)
    fmt.Printf("sum2: %v (%T)\n", sum2, sum2)
}

2.3 泛型Map

type Map[K string | int, V string | int32 | float32] map[K]V

func main() {
    map1 := make(Map[string, int32])
    map2 := make(Map[int, float32])
    map1["a"] = 10
    map1["b"] = 20
    map2[1] = 10.1
    map2[2] = 20.2
    println(map1)
    println(map2)
}

2.4 泛型Struct

type valueType interface {
    int32 | float32
}

type Data[T valueType] struct {
    data []T
}

func (d *Data[T]) addData(newValues ...T) {
    for _, item := range newValues {
        d.data = append(d.data, item)
    }
}
func (d *Data[T]) sum() T {
    var s T
    for _, item := range d.data {
        s += item
    }
    return s
}

type MySlice[T int | float32] []T

func (s MySlice[T]) Sum() T {
    var sum T
    for _, value := range s {
        sum += value
    }
    return sum
}

func main() {
    data1 := []int32{10, 20, 30, 40, 50}
    data2 := []float32{10.1, 20.2, 30.3, 40.4, 50.5}
    d1 := Data[int32]{}
    d2 := Data[float32]{}
    d1.addData(data1...)
    d2.addData(data2...)
    sum1 := d1.sum()
    sum2 := d2.sum()
    fmt.Printf("sum1: %v (%T)\n", sum1, sum1)
    fmt.Printf("sum2: %v (%T)\n", sum2, sum2)

    var s MySlice[int] = []int{1, 2, 3, 4}
    fmt.Println(s.Sum()) // 输出:10
}

2.5 泛型Interface

泛型接口和基本接口(Basic Interface)一般接口(General Interface)不同。而泛型接口要使用的话必须传入类型实参实例化才有意义

type DataProcessor[T any] interface {
    Process(oriData T) (newData T)
    Save(data T)
}

// DataProcessor2[string] 表示约束: 只能是 int 或者 struct{ Data interface{} ;类型
type DataProcessor2[T any] interface {
    int | ~struct{ Data interface{} }

    Process(data T) (newData T)
    Save(data T)
}

type CSVProcessor struct{}

func (c CSVProcessor) Process(oriData string) (newData string) {
    return
}

func (c CSVProcessor) Save(oriData string) {
    return
}

type CustomData struct {
    Name string
}

type CustomDataProcessor struct{ Data interface{} }

func (c CustomDataProcessor) Process(oriData CustomData) (newData CustomData) {
    return
}

func (c CustomDataProcessor) Save(oriData CustomData) {
    return
}

func main() {
    var processor DataProcessor[string] = CSVProcessor{}
    processor.Process("name,age\nbob,12\njack,30")
    processor.Save("name,age\nbob,13\njack,31")

    doSaveCustomData(CustomDataProcessor{})
    //doSaveCustomData(CustomDataProcessor{})

}

func doSaveString[T DataProcessor2[string]](processor T) {
    processor.Save("1")
}

func doSaveCustomData[T DataProcessor2[CustomData]](processor T) {
    processor.Save(CustomData{Name: "2"})
}

三、泛型底层实现

// TODO 

四、参考资料

https://segmentfault.com/a/1190000041634906

https://juejin.cn/post/7106393821943955463