网站首页 > 文章精选 正文
为什么需要泛型?
在Go 1.18之前,当我们需要处理多种数据类型时,通常有以下两种方法:
- 为每种类型编写重复的函数(代码冗余)
- 使用 interface{} 类型(失去类型安全,需要类型断言)
泛型解决了这些问题,让我们可以编写类型安全且可复用的代码。
基础概念
类型参数
在函数名或类型名后使用方括号声明类型参数:
func Name[T any](param T) T { ... }
type Stack[T any] []T
类型约束
使用接口约束类型参数允许的操作:
type Numeric interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64
}
深入示例
1. 泛型函数:最大值计算
package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
// 使用标准库约束
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
// 自定义约束
type Numeric interface {
~int | ~float64
}
func MaxNumeric[T Numeric](a, b T) T {
if a > b {
return a
}
return b
}
func main() {
fmt.Println(Max(3, 5)) // 5
fmt.Println(Max(3.14, 2.71)) // 3.14
fmt.Println(Max("a", "b")) // "b"
fmt.Println(MaxNumeric(10, 20)) // 20
fmt.Println(MaxNumeric(3.5, 2.5)) // 3.5
}
2. 泛型类型:栈实现
package main
import "fmt"
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
func (s *Stack[T]) Size() int {
return len(s.items)
}
func main() {
// 整数栈
intStack := Stack[int]{}
intStack.Push(1)
intStack.Push(2)
intStack.Push(3)
fmt.Println("Integer stack size:", intStack.Size()) // 3
if item, ok := intStack.Pop(); ok {
fmt.Println("Popped:", item) // 3
}
// 字符串栈
stringStack := Stack[string]{}
stringStack.Push("Hello")
stringStack.Push("World")
fmt.Println("String stack size:", stringStack.Size()) // 2
if item, ok := stringStack.Pop(); ok {
fmt.Println("Popped:", item) // "World"
}
}
3. 泛型方法:Map/Filter/Reduce
package main
import "fmt"
// 将切片中的每个元素应用函数
func Map[T, U any](slice []T, f func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = f(v)
}
return result
}
// 过滤切片中的元素
func Filter[T any](slice []T, f func(T) bool) []T {
var result []T
for _, v := range slice {
if f(v) {
result = append(result, v)
}
}
return result
}
// 对切片进行累积计算
func Reduce[T, U any](slice []T, initial U, f func(U, T) U) U {
result := initial
for _, v := range slice {
result = f(result, v)
}
return result
}
func main() {
numbers := []int{1, 2, 3, 4, 5}
// 平方计算
squares := Map(numbers, func(n int) int {
return n * n
})
fmt.Println("Squares:", squares) // [1 4 9 16 25]
// 过滤偶数
evens := Filter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println("Evens:", evens) // [2 4]
// 求和
sum := Reduce(numbers, 0, func(acc, n int) int {
return acc + n
})
fmt.Println("Sum:", sum) // 15
// 字符串处理
words := []string{"go", "generics", "are", "awesome"}
lengths := Map(words, func(s string) int {
return len(s)
})
fmt.Println("Word lengths:", lengths) // [2 9 3 7]
}
泛型 vs 传统方法
传统方法 (使用 interface{})
func MaxInterface(a, b interface{}) interface{} {
switch a.(type) {
case int:
if a.(int) > b.(int) {
return a
}
return b
case float64:
if a.(float64) > b.(float64) {
return a
}
return b
// 需要为每种类型添加case
default:
panic("unsupported type")
}
}
问题:
- 类型不安全(运行时可能panic)
- 需要类型断言
- 添加新类型需要修改代码
- 性能较差(涉及类型断言)
泛型方法
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
优势:
- 编译时类型安全
- 无需类型断言
- 代码更简洁
- 性能接近特定类型实现
最佳实践
- 不要过度使用泛型:仅在真正需要处理多种类型时使用
- 命名约定:使用描述性的类型参数名(如T, K, V)
- 约束设计:创建精确的约束接口
- 避免复杂泛型:保持泛型代码简单易读
- 组合使用:泛型与接口的组合可以创建强大的抽象
实际应用场景
- 集合操作(列表、栈、队列、树)
- 算法实现(排序、搜索、数学计算)
- 数据处理管道(Map/Reduce模式)
- ORM框架(类型安全的查询构建器)
- 测试工具(通用测试辅助函数)
// 通用测试辅助函数示例
func AssertEqual[T comparable](t *testing.T, got, want T) {
t.Helper()
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
// 测试中使用
func TestAddition(t *testing.T) {
result := Add(2, 3)
AssertEqual(t, result, 5)
}
Go的泛型提供了强大的抽象能力,同时保持了Go语言的简洁性和高效性。
猜你喜欢
- 2025-07-14 Go并发编程之WaitGroup(go语言 并发)
- 2025-07-14 golang企业微信告警(企业微信告警推送)
- 2025-07-14 2.8 Go语言中的for循环,break和continue
- 2025-07-14 Go语言Context包:最常用包之一的使用指南
- 2025-07-14 Go语言零到一:动态数组——切片(go语言的切片)
- 2025-07-14 2025-06-26:转换数组。用go语言,给你一个整数数组 nums,它被视
- 2025-07-14 go sync.Pool简介(go system)
- 2025-07-14 2025-07-13:统计特殊子序列的数目。用go语言,给定一个只包含正
- 2025-07-14 Go语言数据库编程:GORM 的基本使用
- 2025-07-14 2025-06-28:长度可被 K 整除的子数组的最大元素和。用go语言,给
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)
- fmt.println (52)