当前位置:   article > 正文

Golang 泛型详解

Golang 泛型详解


Go1.18 版本发布了泛型。


1. 泛型的类型形参

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

package main

import (
	"fmt"
)

// 变量 x, y 为函数的形参(parameter),int 形参类型
func Sum(x, y int) int {
	return x + y
}

func main() {
	var a, b int = 2, 6
	// 变量 a, b 为调用函数的实参(argument)
	fmt.Println(Sum(a, b))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上例 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
}

func main() {
	fmt.Println(Sum[float32](1.1, 2.2))	// 显示指定参数的类型
	fmt.Println(Sum(1, 2))				// 隐式断言
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2. 泛型定义

// 自定义一个新类型 MapT
type MapT[T int|int32|string] map[T]string
  • 1
  • 2

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

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

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

示例如下:


type Map[T interface{int|int32|string}] map[T]string

func 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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

不同类型使用泛型


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
}

// 泛型类型 chan
type ChanT[t T] chan t

// 泛型类型接口
type Animal[t T] interface {
	Running(t)
}

func main() {
	// 下面代码报错。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)
}
  • 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
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

4. ~ :指定底层类型

先看一个示例:

package main

import "fmt"

type T interface { int | int32 | int64 }

type Slice[t T] []t

type MyInt int

func main() {
	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)
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

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

为解决上述问题,Go 新增了一个 ~ 符号;在类型约束中的类型前加 ~ 就表示以该类型为底层类型的类型都可以用于实例化。

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

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

使用 ~ 的限制:

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

3. 类型约束

  • 定义泛型类型时,基础类型不能只有类型形参

    type NewType[T int|float32] T // 报错, cannot use a type parameter as RHS in type declaration
    
    // 改写一下
    // 编译没问题。
    // 虽然使用了类型形参,但类型定义未使用形参,底层类型为 int。
    type NewType[T int|float32] int
    
    func main() {
    	var i NewType[int] = 1	// ok,类型形参 int 满足约束,且赋值满足底层类型 int
    	fmt.Println(i) // 1
    	
    	var f NewType[float32] = 2	// ok,类型形参 float32 满足约束,且赋值满足底层类型 int
    	fmt.Println(f)	// 2
    
    	
    	var 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) 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 约束类型会被编译器误认为表达式

    type NewType[T *int] []T //报错, 指针类型会误认为表达式 * 
    
    // 可在约束后加逗号(,)消除歧义
    type NewType[T *int|*float32,] []T
    
    // 把约束类型包在 interface{} 内(推荐此方法)
    type NewType[T interface { *int|*float32 }] []T
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 约束类型不可重复

    type Int interface {int | ~int} // 报错,int 与 ~int 重复。
    
    • 1
  • 匿名函数、匿名结构体不支持泛型

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/886300
推荐阅读
相关标签
  

闽ICP备14008679号