Go中的泛型:初学者需要了解的作用与使用方法

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:什么是泛型,为什么Go中会出现泛型?

\n

长期以来,Go语言(Golang)一直是少数不支持泛型编程(generics)的现代语言之一。开发者要么为不同的数据类型重复编写代码,要么使用接口和空的interface{},这会导致类型安全性的丧失,并需要在运行时进行类型转换。

\n

随着Go 1.18版本(2022年2月)的发布,该语言终于引入了期待已久的泛型。泛型是一种机制,允许编写适用于任意类型的函数、类型和数据结构,同时在编译阶段保持严格的类型检查。

\n

在本文中,我们将讨论:

\n
    \n
  • Go中为什么需要泛型;
  • \n
  • 如何声明泛型函数和类型;
  • \n
  • 什么是约束(constraints)以及如何使用它们;
  • \n
  • 实际应用示例。
  • \n
\n\n

为什么Go中需要泛型?

\n

在泛型出现之前,一个典型的问题是:需要编写一个能处理不同类型切片(例如int、string、float64)的函数。

\n

没有泛型时,要么编写三个独立的函数:

\n
func 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{}——既不安全也不方便。

\n

有了泛型,只需要一个函数:

\n
func 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
\n\n

Go中泛型的基本语法

\n

声明泛型函数

\n

基本语法使用方括号[]来指定类型参数:

\n
func 函数名[类型名 约束](参数) 返回值 {\n    // 函数体\n}\n
\n

一个返回传入值的简单函数示例:

\n
func 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
\n

Go可以自动推断类型,因此显式指定[int]并非必需:

\n
fmt.Println(Identity(42))       // Go会自动推断 T = int\nfmt.Println(Identity("Go"))    // T = string\n
\n\n

泛型类型(结构体)

\n

泛型不仅适用于函数,也适用于自定义类型:

\n
type 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——这是一个内置的约束,表示“任意类型”。然而,通常需要施加限制:例如,要求类型支持比较或算术运算。

\n

内置

博客

书籍推荐