0


Golang 泛型详解

文章目录


Go1.18 版本发布了泛型。


1. 泛型的类型形参

假如我们需要一个计算两个数之和的函数,如下定义:

package main

import("fmt")// 变量 x, y 为函数的形参(parameter),int 形参类型funcSum(x, y int)int{return x + y
}funcmain(){var a, b int=2,6// 变量 a, b 为调用函数的实参(argument)
    fmt.Println(Sum(a, b))}

上例 Sum 函数仅支持计算 int 类型的数值,无法计算 int32、float32 等类型的和。

若我们把 int、int32、float32 这些类型作为类型形参代替 int 告诉 Sum 我们所计算的类型是什么,就能满足我们的需求。这里就用到了泛型的类型形参。

利用泛型改进一下 Sum 函数

// T:类型形参。调用 Sum 时,需指定 T 的类型值。// [T int|int32|float32] 称为类型形参列表。// int|int32|float32 为类型约束,表示仅支持列出的类型。func Sum[T int|int32|float32](x, y T) T{return x + y
}funcmain(){
    fmt.Println(Sum[float32](1.1,2.2))// 显示指定参数的类型
    fmt.Println(Sum(1,2))// 隐式断言}

2. 泛型定义

// 自定义一个新类型 MapTtype MapT[T int|int32|string]map[T]string

上面代码定义一个新类型 MapT,其中:

  • T:类型形参。定义 MapT 时 T 代表的类型并不确定,类似一个占位符。
  • int|int32|string:类型约束。指定 T 仅可接受哪些类型。
  • 中括号内 T int|int32|string 定义形参类型,称为形参类型列表。

其中,类型约束可以包上 interface{}

示例如下:

type Map[T interface{int|int32|string}]map[T]stringfunc MakeMap[T interface{int|int32|string}](key T, val string) Map[T]{
    m :=make(Map[T])
    m[T]= val
    return m
}// 或简写为type T interface{int|int32|float32|string}// 自定义类型约束type MapT[t T]map[t]string// 可理解为泛型类型的声明,后续声明都使用 t 代替// T 为类型约束;必须已定义,否则报错。// 返回值 MapT[t] 不可改为 Map[t],因两者类型约束不同。func MakeMapT[t T](key t, val string) MapT[t]{
    m :=make(MapT[])
    m[T]= val
    return m
}

不同类型使用泛型

type T interface{int|int32|float32|string|bool}// 泛型类型切片type SliceT [t T][]t

// 泛型类型结构体type StructT[t T]struct{
    id int
    data t
}// 泛型类型结构体方法func(s *StructT[t])SetData(v t){
    s.data = v
}// 泛型类型 chantype ChanT[t T]chan t

// 泛型类型接口type Animal[t T]interface{Running(t)}funcmain(){// 下面代码报错。SliceT[T] 是泛型类型,不能直接使用 T,必须实例化具体的类型。var i SliceT[T]=[]int{1,2,3}// cannot use type T outside a type constraint: interface contains type constraints// 正确使用var s SliceT[string]=[]string{"Hello","World"}
    fmt.Println(s)// 结构体方法
    s :=&StructT[float32]{
            id:1,}
    s.SetData(3.6)
    fmt.Println(s)}

4. ~ :指定底层类型

先看一个示例:

package main

import"fmt"type T interface{int|int32|int64}type Slice[t T][]t

type MyInt intfuncmain(){var s = Slice[int]{1,2,3,4,5}// ok
    fmt.Println(s)// 泛型类型 Slice[T] 允许 int 作为类型实参;而 MyInt 与 int 属于两个类型。var s2 = Slice[MyInt]{1,2,3,4,5}// 报错,MyInt does not satisfy T (possibly missing ~ for int in T)}

上述示例中,泛型类型 Slice[T] 允许 int 作为类型实参,而不是 MyInt。

虽然 MyInt 底层类型是 int,但它属于自定义的一个底层为 int 的新类型,而不是 int 类型。

为解决上述问题,Go 新增了一个

~

符号;在类型约束中的类型前加

~

就表示以该类型为底层类型的类型都可以用于实例化。

把上述代码中的类型约束 T 优化一下就可以正常执行。如下:

type T interface{ ~int| ~int32| ~int64}

使用 ~ 的限制:

  • ~ 后面的类型必须为基本类型。
  • ~ 后面类型不能为接口。

3. 类型约束

  • 定义泛型类型时,基础类型不能只有类型形参type NewType[T int|float32] T // 报错, cannot use a type parameter as RHS in type declaration// 改写一下// 编译没问题。// 虽然使用了类型形参,但类型定义未使用形参,底层类型为 int。type NewType[T int|float32]intfuncmain(){var i NewType[int]=1// ok,类型形参 int 满足约束,且赋值满足底层类型 int fmt.Println(i)// 1var f NewType[float32]=2// ok,类型形参 float32 满足约束,且赋值满足底层类型 int fmt.Println(f)// 2var f2 NewType[float32]=1.2// 报错, 类型形参 float32 满足约束,但实际赋值非 int // cannot use a type parameter as RHS in type declarationcannot use 1.2 // (untyped float constant) as NewType[float32] value in variable declaration (truncated) }
  • 约束类型会被编译器误认为表达式type NewType[T *int][]T //报错, 指针类型会误认为表达式 * // 可在约束后加逗号(,)消除歧义type NewType[T *int|*float32,][]T// 把约束类型包在 interface{} 内(推荐此方法)type NewType[T interface{*int|*float32}][]T
  • 约束类型不可重复type Int interface{int| ~int}// 报错,int 与 ~int 重复。
  • 匿名函数、匿名结构体不支持泛型
标签: golang

本文转载自: https://blog.csdn.net/iuhart/article/details/140680873
版权归原作者 iuhart 所有, 如有侵权,请联系我们删除。

“Golang 泛型详解”的评论:

还没有评论