泛型(Go1.18)

PPG007 ... 2022-3-27 About 3 min

# 泛型(Go1.18)

# 在函数声明中使用泛型

package main

import "fmt"

func main()  {
	fmt.Println(min(1, 2))
	fmt.Println(min[float64](1.2, 2.1))
}
func min[T int|float64](a, b T) T {
	if a < b {
		return a
	}

	return b
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Go1.18 中使用中括号表示泛型,泛型类型 T 的可选类型在后面使用或运算符分割,调用函数时,也可以传入使用该函数的哪个类型(上面的fmt.Println(min[float64](1.2, 2.1))),也可以省略。

如果我们自定义类型(不是起别名),那么将自定义类型传入上面的 min 函数会报错,可以使用~符号,此符号声明的类型可以接收原类型与底层类型是此原类型的类型。

package main

import "fmt"

type myFloat float64

const (
	a myFloat = 1.1
	b myFloat = 2.2
)

func main() {
	fmt.Println(min(a, b))
}
func min[T int | ~float64](a, b T) T {
	if a < b {
		return a
	}

	return b
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

如果需要限制一个方法是某些类型而又不想将中括号写的太长,可以使用自定义接口:

package main

import "fmt"

func main() {
	fmt.Println(min(2, 2))
}

type minType interface {
	int8 | int | int16 | int32 | int64 | uint8 | uint | uint16 | uint32 | uint64 | float32 | float64
}

func min[T minType](a, b T) T {
	if a < b {
		return a
	}
	return b
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

接口中的类型可以换行写,换行后表示要同时具有每一行的类型,例如:

package main

import "fmt"

type myInt int

const (
	a myInt = 1
	b myInt = 2
)

func main() {
	fmt.Println(min(a, b))
}

type minType interface {
	int8 | ~int | int16 | int32 | int64 | uint8 | uint | uint16 | uint32 | uint64 | float32 | float64
	myInt
}

func min[T minType](a, b T) T {
	if a < b {
		return a
	}
	return b
}
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

上面的 myInt 同时满足 ~int以及 myInt 类型。

当然接口也可以同时具有方法:

package main

type myInt int

type minStrct struct {
}

func (minStrct) demo() string {
	return "demo"
}

func main() {
	demo(minStrct{})
}

type demoType interface {
	minStrct
	demo() string
}

func demo[T demoType](a T) {

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Note

如果接口里有类型,那么就不能用来作为变量类型声明变量了,只能用作类型约束。

golang.org/x/exp/constraints 包中,已经定义了一些类型接口,可以直接使用:

type minType interface {
	// int8 | ~int | int16 | int32 | int64 | uint8 | uint | uint16 | uint32 | uint64 | float32 | float64
	myInt
	constraints.Float|constraints.Signed|constraints.Unsigned|constraints.Integer|constraints.Ordered
}
1
2
3
4
5

Note

类型接口前不能使用~符号。

Tips

此外,golang.org/x/exp/slices 包及 golang.org/x/exp/maps 包中分别定义了一些使用泛型的切片及 map 的工具方法,例如:

fmt.Println(slices.Equal([]int{1, 2}, []int{1, 2}))
1

# 结构体泛型

package main

import "fmt"

type myType int

type DemoStructType interface {
	int8 | ~int | int16 | int32 | int64 | uint8 | uint | uint16 | uint32 | uint64 | float32 | float64
}

type DemoStruct[T DemoStructType] struct {
	Data T
}

func main() {
	const m myType = 2
	d := DemoStruct[myType]{
		Data: m,
	}
	fmt.Println(d.Data)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Note

使用泛型结构体声明变量时,结构体的泛型类型不能省略。

# any

any 是一个新的标识符,是 interface{} 的别名:

// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}
1
2

可以使用 any 来代替原来的空接口类型。

# comparable

comparable 是一个接口,表示可以使用 ==!= 进行比较的所有类型的集合:

package main

import "fmt"

func main() {
	fmt.Println(IsEqual(1, 2))
	fmt.Println(IsNotEqual("a", "b"))
}

func IsEqual[T comparable](a, b T) bool {
	return a == b
}

func IsNotEqual[T comparable](a, b T) bool {
	return a != b
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Note

comparable 只能用来限制泛型类型。

# 泛型切片

package main

import "fmt"

type mySlice[T int | string] []T

func main() {
	var a mySlice[int] = []int{1, 2, 3}
	var b mySlice[string] = []string{"1", "2", "3"}
	fmt.Println(a)
	fmt.Println(b)
}
1
2
3
4
5
6
7
8
9
10
11
12

# 泛型 map

package main

import "fmt"

type myMap [K int|string, V float32|float64] map[K]V

func main() {
	var m myMap[int, float32] = map[int]float32{
		1: 1.1,
		2: 2.2,
		3: 3.3,
	}
	fmt.Println(m)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 泛型 channel

package main

import "fmt"

type myChannel[T int|string|[]int] chan T

func main() {
	var m myChannel[[]int] = make(myChannel[[]int])
	defer close(m)
	go func ()  {
		select {
		case x := <- m:
			fmt.Println(x)
			break
		default:

		}
	}()
	m <- []int{1, 2, 3, 4, 5}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Go1.18 其他内容

Go1.18 修改并增加了一些方法,引入了 workspace 及模糊测试,详见:Go1.18 Release Notes (opens new window)

Last update: March 27, 2022 15:32
Contributors: PPG007