Go 中的泛型:为什么需要以及如何在实际中使用

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

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

开始课程

引言:为什么 Go 中的泛型是一个突破?



长期以来,Go 语言 (Golang) 一直是少数不支持泛型编程 (generics) 的现代语言之一。开发者不得不使用 interface{} 和类型转换,这导致了编译时类型安全性的丧失,并使代码变得臃肿。随着 Go 1.18 版本(2022 年 2 月)的发布,情况发生了根本性的变化。



泛型 (Generics) 是一种机制,它允许编写能够处理任何类型的函数和数据结构,同时保持严格的类型检查。在本文中,我们将探讨为什么需要它们,并通过实际示例展示如何在日常 Go 开发中使用它们。



为什么 Go 中需要泛型?



在泛型出现之前,为了创建通用函数,开发者使用 interface{}(空接口)。考虑一个经典示例——一个在切片中查找最小元素的函数:



// 没有泛型 (Go < 1.18)func MinInt(a, b int) int {    if a < b {        return a    }    return b}

func MinFloat(a, b float64) float64 { if a < b { return a } return b}


对于每种类型,都必须编写单独的函数。使用 interface{} 可以这样做:



func MinInterface(a, b interface{}) interface{} {    // 需要类型转换和运行时检查    // 这既不安全也不高效}


这种方法的缺点显而易见:

  • 类型安全性的丧失——错误可能仅在运行时才显现。
  • 需要类型转换——代码变得冗长。
  • 性能下降——与接口交互需要额外的操作。


泛型解决了所有这些问题。它们允许:

  • 一次编写通用算法。
  • 在编译时保持严格的类型检查。
  • 消除类型转换并提高性能。


Go 中泛型的基本语法



Go 中的泛型基于类型参数。语法简单直观。类型参数在函数或类型名称后的方括号中指定。



1. 带泛型的简单函数



让我们编写相同的求最小值函数,但现在使用泛型:



package main

import "fmt"

// Min 返回两个值中的最小值func Min[T comparable](a, b T) T { if a < b { return a } return b}

func main() { fmt.Println(Min[int](10, 20)) // 10 fmt.Println(Min[float64](3.14, 2.71)) // 2.71 fmt.Println(Min[string]("abc", "xyz")) // abc}


这里的 [T comparable] 是类型参数。comparable 是一个内置约束,它保证类型支持 == 和 != 比较运算符。请注意:对于 < 运算符,需要使用 constraints.Ordered 约束(来自 golang.org/x/exp/constraints 包)或编写自己的约束。



2. 约束 (Constraints)



约束是定义类型必须支持哪些方法和操作的接口。在 Go 1.18+ 中,引入了 constraints 包(目前位于 golang.org/x/exp 中),但您也可以创建自己的约束。



import "golang.org/x/exp/constraints"

// MinOrdered 仅适用于有序类型func MinOrdered[T constraints.Ordered](a, b T) T { if a < b { return a } return b}


针对支持 String() 方法的类型的自定义约束:



博客

书籍推荐