跳到主要内容

泛型

前言

Go在1.18版本中正式带来泛型特性。

什么是泛型

泛型(Generic)是编程中的一种机制,允许类、接口和方法能够在定义时使用占位符来表示数据类型,从而使代码能够适应多种不同的数据类型,而无需重复编写相似的代码。泛型主要用于提高代码的复用性、可读性和安全性。泛型最常见的应用是在静态类型语言(如Java、C#、C++等)中,通过为类或方法定义参数化类型,可以在实例化时指定具体的类型。这意味着可以编写一次逻辑,然后在多个不同的数据类型上重复使用。

泛型的优点:

  • 类型安全:通过指定类型参数,编译器可以在编译时检查类型是否匹配,减少运行时错误。
  • 代码复用:可以编写更加通用的代码,而不必为每种数据类型编写重复的代码。
  • 可读性和可维护性:减少代码冗余,提高可读性,代码的维护也变得更加容易。

泛型的声明

  • 泛型切片
type Slice1 [T int|float64|string] []T

// 可以指代下面三种的切片类型
type SliceInt []int
type SliceFloat []float64
type SliceInt []string
  • 泛型哈希表
type Map1 [KEY int|string, VALUE string| float64] map[KEY]VALUE

// 相当于
type Map2 map[int]string
type Map3 map[int]float64
type Map4 map[string]string
type Map5 map[string]float64
  • 泛型结构体
type Struct1 [T string|int|float64] struct {
Title string
Content T
}

// 相当于
// 结构体
type Struct3 struct {
Title string
Content string
}

type Struct4 struct {
Title string
Content int
}

type Struct5 struct {
Title string
Content float64
}
  • 泛型方法。下面的泛型方法可以灵活地对任意类型的 Data 进行加锁,并执行 f 函数
type Lockable[T any] struct {
mu sync.Mutex
data T
}

func (l *Lockable[T]) Do(f func(*T)) {
l.mu.Lock()
defer l.mu.Unlock()
f(&l.data)
}
  • 泛型函数
// 函数 NoDiff 可以判断可变长度数组中的每一个元素是不是都是相同的。
func NoDiff[V comparable](vs ...V) bool {
if len(vs) == 0 {
return true
}

v := vs[0]
for _, x := range vs[1:] {
if v != x {
return false
}
}
return true
}
  • 泛型接口
type MyInterface[T int | string] interface {
WriteOne(data T) T
ReadOne() T
}

泛型的类型约束

类型约束 是指定类型参数允许的类型范围的一种机制。在 Go 中,类型约束 是通过 接口 来定义的。接口不仅可以定义行为,还可以约束泛型参数必须满足的类型特性。类型参数用方括号 [] 包围,并放在函数、结构体、接口定义的后面。类型约束通过接口类型来指定类型参数的合法范围。

基本类型约束:通过~运算符对基本类型进行约束。在下面例子中,~int~float64 表示任何底层类型为 intfloat64 的类型都符合 Number 约束。

type MyInt int

type Number interface {
~int | ~float64
}

func Add[T Number](a, b T) T {
return a + b
}

Go还提供了一些预定义约束,

  • any:可以接受任何类型,等价于空接口 interface{}
  • comparable:要求类型可以进行比较操作(使用 ==!= 操作符)。
  • ~T:表示底层类型为 T 的所有类型。

泛型的实例化

  • 泛型切片实例化
type Slice1 [T int|float64|string] []T
var MySlice1 Slice1[int] = []int{1,2,3}
var MySlice3 Slice1[string] = []string{"hello", "small", "yang"}
var MySlice5 Slice1[float64] = []float64{1.222, 3.444, 5.666}
  • 泛型map实例化
type Map1[KEY int | string, VALUE string | float64] map[KEY]VALUE

var MyMap1 Map1[int, string] = map[int]string{
1: "hello",
2: "small",
}

var MyMap3 Map1[string, string] = map[string]string{
"one": "hello",
"two": "small",
}
  • 泛型结构体实例化
type Aticle [T string|int|float64] struct {
Title string
Content T
}

var s = Aticle[string]{
Title: "hello",
Content: "small",
}

// 复杂结构体的实例化
type MyStruct[S int | string, P map[S]string] struct {
Name string
Content S
Job P
}

var MyStruct1 = MyStruct[int, map[int]string]{
Name: "small",
Content: 1,
Job: map[int]string{1: "ss"},
}
  • 泛型函数实例化
// 函数实例化
package main
func NoDiff[V comparable](vs ...V) bool {
if len(vs) == 0 {
return true
}

v := vs[0]
for _, x := range vs[1:] {
if v != x {
return false
}
}
return true
}

func main() {
var NoDiffString = NoDiff[string]
println(NoDiffString("Go", "go")) // false
println(NoDiff[int](123, 123, 789)) // false
}
// 函数实例化,例子2
type Ordered interface {
~int | ~uint | ~int8 | ~uint8 | ~int16 | ~uint16 |
~int32 | ~uint32 | ~int64 | ~uint64 | ~uintptr |
~float32 | ~float64 | ~string
}

func Max[S ~[]E, E Ordered](vs S) E {
if len(vs) == 0 {
panic("no elements")
}

var r = vs[0]
for i := range vs[1:] {
if vs[i] > r {
r = vs[i]
}
}
return r
}

type Age int
var ages = []Age{99, 12, 55, 67, 32, 3}

var langs = []string {"C", "Go", "C++"}

func main() {
var maxAge = Max[[]Age, Age]
println(maxAge(ages)) // 99

var maxStr = Max[[]string, string]
println(maxStr(langs)) // Go
}
  • 泛型方法实例化
package main

import "sync"

type Lockable[T any] struct {
mu sync.Mutex
data T
}

func (l *Lockable[T]) Do(f func(*T)) {
l.mu.Lock()
defer l.mu.Unlock()
f(&l.data)
}

func main() {
var n Lockable[uint32]
n.Do(func(v *uint32) {
*v++
})

var f Lockable[float64]
f.Do(func(v *float64) {
*v += 1.23
})

var b Lockable[bool]
b.Do(func(v *bool) {
*v = !*v
})

var bs Lockable[[]byte]
bs.Do(func(v *[]byte) {
*v = append(*v, "Go"...)
})
}

// 方法实例化,例子2
type Number interface{
int | int32 | int64 | float64 | float32
}

//定义一个泛型结构体,表示堆栈
type Stack[V Number] struct {
size int
value []V
}

//加上Push方法
func (s *Stack[V]) Push(v V) {
s.value = append(s.value, v)
s.size++
}

//加上Pop方法
func (s *Stack[V]) Pop() V {
e := s.value[s.size-1]
if s.size != 0 {
s.value = s.value[:s.size-1]
s.size--
}
return e
}

//实例化成一个int型的结构体堆栈
s1 := &Stack[int]{}

//入栈
s1.Push(1)
s1.Push(2)
s1.Push(3)
fmt.Println(s1.size, s1.value) // 3 [1 2 3]

//出栈
fmt.Println(s1.Pop()) //3
fmt.Println(s1.Pop()) //2
fmt.Println(s1.Pop()) //1
  • 泛型接口实例化
type MyInterface[T int | string] interface {
WriteOne(data T) T
ReadOne() T
}

type Note struct {

}

func (n Note) WriteOne(one string) string {
return "hello"
}

func (n Note) ReadOne() string {
return "small"
}

var one MyInterface[string] = Note{}
fmt.Println(one.WriteOne("hello"))
fmt.Println(one.ReadOne())

自动类型推断

// 函数实例化
package main
func NoDiff[V comparable](vs ...V) bool {
if len(vs) == 0 {
return true
}

v := vs[0]
for _, x := range vs[1:] {
if v != x {
return false
}
}
return true
}

func main() {
println(NoDiff("Go", "Go", "Go")) // true,自动推断
println(NoDiff[string]("Go", "go")) // false

println(NoDiff(123, 123, 123, 123)) // true, 自动推断
println(NoDiff[int](123, 123, 789)) // false

type A = [2]int
println(NoDiff(A{}, A{}, A{})) // true, 自动推断
println(NoDiff(A{}, A{}, A{1, 2})) // false,自动推断

println(NoDiff(new(int))) // true,自动推断
println(NoDiff(new(int), new(int))) // false,自动推断
}