前言
在之前都未接触过泛型,在之前偶然听别人提及过泛型这东西,所以就学习总结一下go的泛型使用
一、为什么泛型会在新版的go中加入?
一个简单的例子来比较用泛型和不用泛型的区别
- 需求:封装一个函数来实现对多种类型(int、float...)进行加法运算
由于函数的入参的类型只能定义一个
func sumInt(a, b int) int {
return a + b
}
所以我们只能使用interface作为入参在利用反射进行类型判断来实现
func sum_Int_Float(a, b interface{
}) interface{
} {
switch a.(type) {
case int:
a1 := a.(int)
b1 := b.(int)
return a1 + b1
case float64:
a1 := a.(float64)
b1 := b.(float64)
return a1 + b1
default:
return nil
}
}
但是使用泛型的话那么将是这个样子
func sum_Int_Float[T int|float64](a,b T) T {
return a + b
}
这样看下来使用泛型会简洁很多
二、泛型语法详解
一个简单的泛型大概是这样的
func MyPrintln[T any](a T) {
fmt.Println(a)
}
func main() {
MyPrintln("nb")
//运行结果:
//nb
}
MyType[T1 constraint1 | constraint2, T2 constraint3...] ...
- MyType可以是函数名, 结构体名, 类型名…
- T1, T2…是泛型名, 可以随便取
- constraint的意思是约束,是泛型中最重要的概念, T满足其中之一即可(如T1可以是constraint1和constraint2中的任何一个)
三、constraint约束
在之前的例子中func MyPrintln[T any](a T)
any就是一个约束
不过看any的底层代码type any = interface{}
可知any就跟interface一样
而go中的约束大概是有这些
any(interface{}
Interger
Float
comparable (可比较)
...
自定义constraint
然后我们可以看看constraint包里的约束是怎么构造的,然后我们就可以自定义constraint(但是正式版的constraints已经被去除掉了,详细原因)
// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
~float32 | ~float64
}
//......
由此我们可知道怎样去自定义约束了
例如我们想定义一个map,限制它的k,v的类型
type myCompare interface {
~int | ~float64 | [5]interface{
} | struct{
}
}
type myint interface {
~int8|~int64
}
type myfloat interface {
~float64|~float32
}
type MyMap[K myCompare, V myfloat | myint] map[K]V
这样子我们就定义了一个自己的map
- 除map外,定义泛型结构体变量:
type Struct1 [T string|int|float64] struct {
Title string
Content T
}
而对于结构体,结构体可以进行匿名操作
即把结构体的申明定义和初始化一起完成,举个例子
stu := struct{
Name string
Age int
Weight float64
}{
"smallyang",
18,
50.5,
}
fmt.Println("Student =", stu) // Student = {smallyang 18 50.5}
但是如果对泛型定义的结构体是不支持匿名的
stu2 := struct[T int|float64] {
Name string
Age int
Weight T
}[int]{
"smallyang",
18,
50,
}
fmt.Println("Student =", stu2)
/*
./main.go:70:16: syntax error: unexpected [, expecting {
./main.go:72:10: syntax error: unexpected int at end of statement
./main.go:73:10: syntax error: unexpected T at end of statement
./main.go:74:3: syntax error: unexpected [ after top level declaration
*/
- 泛型数组变量:
type slice[T any] []T
等等...
四、泛型中操作各种数据类型的例子示范
1、操作slice
package main
import (
"fmt"
)
type slice[T any] []T
type mySlice interface {
自定义constraint
~int | ~string
}
func printSlice[T mySlice](s []T) {
for _, v := range s {
fmt.Printf("%v ", v)
}
fmt.Print("\n")
}
func main() {
vs := slice[int]{
1, 2, 3, 4}
printSlice(vs)
vs2 := slice[string]{
"a", "b"}
printSlice(vs2)
}
2、操作指针
package main
import (
"fmt"
)
func pointerOf[T any](v T) *T {
return &v
}
func main() {
sp := pointerOf("foo")
fmt.Println(*sp)
ip := pointerOf(123)
fmt.Println(*ip)
*ip = 234
fmt.Println(*ip)
}
3、操作map
package main
import (
"fmt"
)
func mapFunc[T any, M any](a []T, f func(T) M) []M {
n := make([]M, len(a), cap(a))
for i, e := range a {
n[i] = f(e)
}
return n
}
func main() {
vi := []int{
1, 2, 3, 4, 5, 6}
vs := mapFunc(vi, func(v int) string {
return "<" + fmt.Sprint(v*v) + ">"
})
fmt.Println(vs)
}