news 2026/4/26 14:51:44

Go 语言从入门到进阶 | 第 7 章:泛型(Generics)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Go 语言从入门到进阶 | 第 7 章:泛型(Generics)

系列:Go 语言从入门到进阶
作者:耿雨飞
适用版本:go v1.26.2


前置条件

在开始本章学习之前,请确保:

  • 已完成第 1 ~ 6 章的学习,掌握接口、类型断言和类型 switch
  • 理解接口的隐式实现机制和方法集规则
  • 已获取 Go 1.26.2 源码树(go-go1.26.2目录)

导读

Go 1.18 引入了泛型(Generics),这是 Go 语言自诞生以来最大的语法扩展。在此之前,通用数据结构和算法只能通过interface{}或代码生成实现,要么牺牲类型安全,要么增加维护成本。泛型的引入让我们可以编写类型安全的通用代码,同时保持 Go 一贯的简洁风格。

Go 的泛型设计独具特色——它使用**类型约束(Type Constraints)**来限制类型参数的能力范围,而类型约束本身就是接口。这意味着泛型与 Go 已有的接口体系无缝融合,而非引入一套全新的概念。

本章将从泛型基础语法出发,逐步深入到类型约束的进阶用法,最后结合slicesmapscmp三个标准库泛型包的源码,展示泛型在实际工程中的应用。

本章将对照 Go 1.26.2 源码中的以下关键路径:

源码路径内容说明
src/builtin/builtin.go:97any约束的定义——interface{}的别名
src/builtin/builtin.go:104comparable约束的定义
src/cmp/cmp.gocmp.Ordered约束、cmp.Comparecmp.Lesscmp.Or函数
src/slices/slices.go泛型切片操作:EqualIndexContainsInsertDeleteCloneCompactReverse
src/slices/sort.go泛型排序:SortSortFuncBinarySearchMinMax
src/maps/maps.go泛型映射操作:EqualCloneCopyDeleteFunc
src/maps/iter.go泛型映射迭代器:AllKeysValuesInsertCollect

学习目标

完成本章后,你将能够:

  1. 理解类型参数和类型约束的语法,编写泛型函数和泛型类型
  2. 掌握anycomparable两个内建约束的含义与适用场景
  3. 理解~近似约束符的作用,能够定义自定义约束接口
  4. 从源码层面理解cmp.Ordered约束的设计
  5. 熟练使用slicesmapscmp标准库泛型包
  6. 理解泛型与接口的关系,做出合理的设计选择

7.1 泛型基础

7.1.1 为什么需要泛型

在泛型引入之前,编写通用函数有两种选择,都不理想:

方式一:为每种类型重复编写

funcContainsInt(s[]int,vint)bool{for_,x:=ranges{ifx==v{returntrue}}returnfalse}funcContainsString(s[]string,vstring)bool{for_,x:=ranges{ifx==v{returntrue}}returnfalse}// 还有 ContainsFloat64、ContainsBool... 无穷无尽

方式二:使用 interface{},牺牲类型安全

funcContains(s[]any,v any)bool{for_,x:=ranges{ifx==v{returntrue}}returnfalse}// 调用时需要手动转换类型,且编译器无法检查类型匹配// Contains([]any{1, 2, 3}, "hello") // 编译通过,但逻辑错误

方式三(Go 1.18+):泛型——类型安全且通用

funcContains[E comparable](s[]E,v E)bool{for_,x:=ranges{ifx==v{returntrue}}returnfalse}// 编译期类型安全Contains([]int{1,2,3},42)// OKContains([]string{"a","b"},"b")// OK// Contains([]int{1, 2, 3}, "hello") // 编译错误!类型不匹配

7.1.2 类型参数(Type Parameters)

类型参数是泛型的核心语法。在函数名或类型名后用方括号[]声明类型参数:

func函数名[T 约束](参数列表)返回值列表{// T 在函数体中可以当做类型使用}
packagemainimport"fmt"// 单个类型参数funcPrint[T any](v T){fmt.Println(v)}// 多个类型参数funcPair[K,V any](k K,v V)string{returnfmt.Sprintf("(%v, %v)",k,v)}// 类型参数用于返回值funcZero[T any]()T{varzero Treturnzero}funcmain(){// 显式指定类型参数Print[int](42)Print[string]("hello")// 类型推断——编译器自动推断 TPrint(3.14)// T 推断为 float64Print(true)// T 推断为 boolfmt.Println(Pair("name",42))// (name, 42)fmt.Println(Zero[int]())// 0fmt.Println(Zero[string]())// ""fmt.Println(Zero[bool]())// false}

类型推断规则:编译器根据传入的实参类型推断类型参数。大多数情况下无需显式指定类型参数,但在以下场景中需要显式指定:

  • 类型参数只出现在返回值(如Zero[int]()
  • 需要消除歧义时

7.1.3 类型约束(Type Constraints)

类型约束限制了类型参数可以接受的类型范围。在 Go 中,类型约束就是接口

// any 约束:接受任何类型funcPrint[T any](v T){...}// comparable 约束:接受所有可比较的类型(支持 == 和 !=)funcContains[E comparable](s[]E,v E)bool{...}// 自定义接口约束typeStringerinterface{String()string}funcPrintString[T Stringer](v T){fmt.Println(v.String())}

7.1.4 any 约束

anyinterface{}的别名,定义在src/builtin/builtin.go:97

// src/builtin/builtin.go:96-97// any is an alias for interface{} and is equivalent to interface{} in all ways.typeany=interface{}

any约束允许传入任何类型,但这也意味着在函数体内你只能对T做最基本的操作——赋值、取地址、作为any使用:

funcIdentity[T any](v T)T{returnv// OK:赋值和返回}funcBroken[T any](a,b T)bool{// return a == b // 编译错误!any 不保证支持 ==// return a + b // 编译错误!any 不保证支持 +_=a_=breturnfalse}

7.1.5 comparable 约束

comparable是一个特殊的内建约束,定义在src/builtin/builtin.go:99-104

// src/builtin/builtin.go:99-104// comparable is an interface that is implemented by all comparable types// (booleans, numbers, strings, pointers, channels, arrays of comparable types,// structs whose fields are all comparable types).// The comparable interface may only be used as a type parameter constraint,// not as the type of a variable.typecomparableinterface{comparable}

comparable约束允许对类型参数使用==!=运算符:

packagemainimport"fmt"funcIndexOf[E comparable](s[]E,v E)int{fori,x:=ranges{ifx==v{// comparable 保证了 == 可用returni}}return-1}funcmain(){fmt.Println(IndexOf([]int{10,20,30},20))// 1fmt.Println(IndexOf([]string{"a","b","c"},"b"))// 1fmt.Println(IndexOf([]float64{1.1,2.2,3.3},2.2))// 1// 以下不能编译——切片不是 comparable 类型// IndexOf([][]int{ {1}, {2}}, []int{1}) // 编译错误}

comparable 涵盖的类型:

类型类别示例可比较
布尔boolYes
数值int,float64,complex128Yes
字符串stringYes
指针*int,*MyStructYes
Channelchan intYes
数组[3]int(元素可比较时)Yes
结构体struct{X int}(字段都可比较时)Yes
切片[]intNo
Mapmap[string]intNo
函数func()No

7.2 泛型函数与泛型类型

7.2.1 泛型函数定义与调用

泛型函数可以有复杂的类型参数和约束组合:

packagemainimport"fmt"// 返回两个值中较大的那个funcMax[Tinterface{~int|~float64|~string}](a,b T)T{ifa>b{returna}returnb}// 过滤切片:返回满足条件的元素funcFilter[T any](s[]T,ffunc(T)bool)[]T{varresult[]Tfor_,v:=ranges{iff(v){result=append(result,v)}}returnresult}// Map 转换:将 []T 转为 []UfuncMap[T,U any](s[]T,ffunc(T)U)[]U
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 14:51:43

如何快速将Amlogic电视盒子改造为Armbian服务器:完整教程指南

如何快速将Amlogic电视盒子改造为Armbian服务器:完整教程指南 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s905, s905l, r…

作者头像 李华
网站建设 2026/4/26 14:47:12

终极指南:5分钟上手Translumo,Windows最强实时屏幕翻译神器

终极指南:5分钟上手Translumo,Windows最强实时屏幕翻译神器 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Transl…

作者头像 李华
网站建设 2026/4/26 14:42:57

终极Fusion 360 3D打印螺纹优化指南:告别螺纹打印失败

终极Fusion 360 3D打印螺纹优化指南:告别螺纹打印失败 【免费下载链接】Fusion-360-FDM-threads 项目地址: https://gitcode.com/gh_mirrors/fu/Fusion-360-FDM-threads 还在为3D打印螺纹总是失败而烦恼吗?Fusion-360-FDM-threads项目为您提供了…

作者头像 李华
网站建设 2026/4/26 14:36:54

网页文本批量替换神器:告别繁琐手动操作,效率提升10倍

网页文本批量替换神器:告别繁琐手动操作,效率提升10倍 【免费下载链接】chrome-extensions-searchReplace 项目地址: https://gitcode.com/gh_mirrors/ch/chrome-extensions-searchReplace 还在为网页文本修改而烦恼吗?无论是批量更新…

作者头像 李华