引言:什么是泛型,为什么Go中会出现泛型?
\n长期以来,Go语言(Golang)一直是少数不支持泛型编程(generics)的现代语言之一。开发者要么为不同的数据类型重复编写代码,要么使用接口和空的interface{},这会导致类型安全性的丧失,并需要在运行时进行类型转换。
随着Go 1.18版本(2022年2月)的发布,该语言终于引入了期待已久的泛型。泛型是一种机制,允许编写适用于任意类型的函数、类型和数据结构,同时在编译阶段保持严格的类型检查。
\n在本文中,我们将讨论:
\n- \n
- Go中为什么需要泛型; \n
- 如何声明泛型函数和类型; \n
- 什么是约束(constraints)以及如何使用它们; \n
- 实际应用示例。 \n
为什么Go中需要泛型?
\n在泛型出现之前,一个典型的问题是:需要编写一个能处理不同类型切片(例如int、string、float64)的函数。
没有泛型时,要么编写三个独立的函数:
\nfunc SumInts(nums []int) int {\n total := 0\n for _, v := range nums {\n total += v\n }\n return total\n}\n\nfunc SumFloats(nums []float64) float64 {\n total := 0.0\n for _, v := range nums {\n total += v\n }\n return total\n}\n\n要么使用带有类型转换的interface{}——既不安全也不方便。
有了泛型,只需要一个函数:
\nfunc Sum[T int | float64](nums []T) T {\n var total T\n for _, v := range nums {\n total += v\n }\n return total\n}\n\n优势显而易见:
\n- \n
- 类型安全——类型错误在编译阶段就能被捕获; \n
- 减少代码重复——一个函数适用于多种类型; \n
- 可读性——代码更加简洁明了。 \n
Go中泛型的基本语法
\n声明泛型函数
\n基本语法使用方括号[]来指定类型参数:
func 函数名[类型名 约束](参数) 返回值 {\n // 函数体\n}\n\n一个返回传入值的简单函数示例:
\nfunc Identity[T any](value T) T {\n return value\n}\n\nfunc main() {\n fmt.Println(Identity[int](42)) // 42\n fmt.Println(Identity[string]("Go")) // Go\n}\n\nGo可以自动推断类型,因此显式指定[int]并非必需:
fmt.Println(Identity(42)) // Go会自动推断 T = int\nfmt.Println(Identity("Go")) // T = string\n\n\n泛型类型(结构体)
\n泛型不仅适用于函数,也适用于自定义类型:
\ntype Stack[T any] struct {\n elements []T\n}\n\nfunc (s *Stack[T]) Push(value T) {\n s.elements = append(s.elements, value)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n if len(s.elements) == 0 {\n var zero T\n return zero, false\n }\n value := s.elements[len(s.elements)-1]\n s.elements = s.elements[:len(s.elements)-1]\n return value, true\n}\n\nfunc main() {\n intStack := Stack[int]{}\n intStack.Push(10)\n intStack.Push(20)\n val, ok := intStack.Pop()\n fmt.Println(val, ok) // 20 true\n}\n\n\n约束:类型限制
\n在上面的示例中,我们使用了any——这是一个内置的约束,表示“任意类型”。然而,通常需要施加限制:例如,要求类型支持比较或算术运算。