一、前言
go1.18版本已经发布有几天了, 随着1.18的发布,大家呼吁已久的泛型也与大家正式见面了。趁着这节假日之际,学习整理范型的用法。
在我们以往中,如果要声明一个带参数的函数,这个函数的参数类型可以是任意类型,我们要怎么做呢?我们肯定想到的是interface{}
。
在go中,any
表示泛型,那么泛型底层是怎么做的呢?在builtin/builtin.go
文件中,我们可以看到type any = interface{}
的声明,原来它底层也是interface{}。
二、常规操作
1、泛型切片
下面我们定义一个底层类型为切类型的新类型,它是可以存储任意类型的切片。
// 定义泛型切片vector
type vector[T any] []T // [T any]参数的类型
func main() {
v1 := vector[int]{1, 2, 3, 4, 5}
fmt.Println(v1) // [1 2 3 4 5]
v2 := vector[string]{"李志", "谢天笑", "老狼"}
fmt.Println(v2) // [李志 谢天笑 老狼]
v3 := vector[float64]{2.22, 3.33, 4.44}
fmt.Println(v3) // [2.22 3.33 4.44]
}
2、泛型map
type M[K string, V any] map[K]V // 这里的K不支持any,由于map底层是hash
func main() {
m1 := M[string, int]{"number": 21}
m1["number"] = 33
fmt.Println(m1)
m2 := M[string, string]{"name": "李志"}
m2["key"] = "老狼"
fmt.Println(m2)
}
3、声明一个泛型chan
type C[T any] chan T
func main() {
c1 := make(C[int], 2)
c1 <- 10
c1 <- 20
close(c1)
for c := range c1 {
fmt.Println(c)
}
c2 := make(C[string], 2)
c2 <- "李志"
c2 <- "老狼"
close(c2)
for c := range c2 {
fmt.Println(c)
}
}
4、声明一个泛型函数
func foo[T any](s T) {
fmt.Println(s)
}
func main() {
foo[string]("李志") // 显著指定参数的类型
foo(123)
foo([3]string{"朱格乐", "阿玛尼", "张怡然"})
// 这个也是显著指定类型,也可以不指定,指定了个人感觉看着好烦琐。。。
foo[[]string]([]string{"李志", "谢天笑", "木马", "张玮玮"})
foo(map[string]any{
"name": "李志",
"age": 43,
"address": "南京",
})
}
三、范型约束
1、使用interface中规定的类型约束范型函数的参数
type NumStr interface {
Num | Str
}
type Num interface {
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128
}
type Str interface {
string
}
func add[T NumStr](a, b T) T {
return a + b
}
func main() {
fmt.Println(add(3, 4))
fmt.Println("hello", "world")
}
说明:NumStr是我们新增的类型列表表达式,它是对类型参数进行约束的,使用|
表示取并集。如果传入的参数不在集合限制范围内,则会抛出错误,另外,类型不能混用。
2、使用interface中的规定的方法来约束泛型的参数
type Num1 int
func (n1 Num1) String() string {
return strconv.Itoa(int(n1))
}
type Num2 string
func (n2 Num2) String() string {
return string(n2)
}
type ShowNum interface {
String() string
~int | ~string
}
func ShowNumList[T ShowNum](s []T) (result []string) {
for _, v := range s {
result = append(result, v.String())
}
return
}
func main() {
fmt.Println(ShowNumList([]Num1{1, 2, 3, 4, 5}))
fmt.Println(ShowNumList([]Num2{"1", "2", "3", "4", "5"}))
}
3、使用comparable约束
comparable是由所有可比较类型实现的接口(布尔、数字、字符串、指针、通道、类似类型的数组,字段均为可比较类型的结构)。可比接口只能用作类型参数约束,不是作为变量的类型。
func foo[T comparable](a []T, b T) int {
for i, v := range a {
if v == b {
return i
}
}
return 0
}
func main() {
fmt.Println(foo([]int{1, 2, 3, 4, 5, 6}, 2))
fmt.Println(foo([]string{"李大鹅", "李志"}, "李志"))
}