一、基础知识
1.1 形参和实参
func min(a, b int) int {
if a > b {
return b
}
return a
}
func main() {
minNum := min(100, 200)
}
如上a
、b
叫形参(parameter
),100
和200
叫实参(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
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