初探泛型

一、前言

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{"李大鹅", "李志"}, "李志"))
}
posted @ 2022-04-04 22:36  李大鹅  阅读(35)  评论(0编辑  收藏  举报