泛型
泛型是 Go 1.18 引入的特性,让开发者能编写不依赖特定数据类型的可复用代码。核心是两个概念:
| 概念 | 作用 | 示例 |
|---|---|---|
| 类型参数(Type Parameters) | 在函数或类型名后声明待定类型 | [T any] |
| 类型约束(Type Constraints) | 规定类型参数必须满足的条件 | int、comparable、any |
| any | 任意类型 | [T any] |
| comparable | 可比较的类型(支持 == 和 !=) | [K comparable] |
为什么需要泛型
传统写法需要为每种类型写重复的函数:
// 处理 int 类型
func MaxInt(a, b int) int {
if a > b {
return a
}
return b
}
// 处理 float64 类型,几乎一样的代码
func MaxFloat(a, b float64) float64 {
if a > b {
return a
}
return b
}
使用泛型后,一个函数处理多种类型:
// 一个函数处理多种类型
func Max[T comparable](a, b T) T {
if a > b {
return a
}
return b
}
泛型语法
泛型函数和类型通过类型参数列表声明,语法为 [类型参数 约束]:
func 函数名[T 约束](参数 T) 返回值类型 {
// 函数体
}
type 类型名[T 约束] struct {
// 结构体字段
}
类型参数命名约定:通常用大写字母,T 表示 Type,K 表示 Key,V 表示 Value,E 表示 Element。
内置约束
any 约束
any 是空接口 interface{} 的别名,表示任何类型都可以。
package main
import "fmt"
func PrintAny[T any](value T) {
fmt.Printf("Value: %v, Type: %T\n", value, value)
}
func main() {
PrintAny(42) // Value: 42, Type: int
PrintAny("hello") // Value: hello, Type: string
PrintAny(3.14) // Value: 3.14, Type: float64
}
comparable 约束
表示类型支持 == 和 != 操作符,可用来做相等判断。
package main
import "fmt"
func FindIndex[T comparable](slice []T, target T) int {
for i, v := range slice {
if v == target {
return i
}
}
return -1
}
func main() {
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(FindIndex(numbers, 3)) // 2
names := []string{"Alice", "Bob", "Charlie"}
fmt.Println(FindIndex(names, "Bob")) // 1
}
联合约束
使用 | 运算符把多个类型组合成一个约束。
package main
import "fmt"
// 数字类型约束
type Number interface {
int | int8 | int16 | int32 | int64 |
uint | uint8 | uint16 | uint32 | uint64 |
float32 | float64
}
func Add[T Number](a, b T) T {
return a + b
}
func main() {
fmt.Println(Add(10, 20)) // 30
fmt.Println(Add(3.14, 2.71)) // 5.85
}
自定义约束:方法约束
定义需要特定方法的约束,约束类型必须实现指定方法。
package main
import "fmt"
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func PrintString[T Stringer](value T) {
fmt.Println(value.String())
}
func main() {
person := Person{Name: "Alice", Age: 25}
PrintString(person) // Alice (25 years old)
}
泛型函数示例
package main
import "fmt"
// 交换两个值
func Swap[T any](a, b T) (T, T) {
return b, a
}
// 判断切片是否包含元素
func Contains[T comparable](slice []T, target T) bool {
for _, item := range slice {
if item == target {
return true
}
}
return false
}
func main() {
a, b := Swap(10, 20)
fmt.Println(a, b) // 20 10
numbers := []int{1, 2, 3, 4, 5}
fmt.Println(Contains(numbers, 3)) // true
}
泛型在编译时进行类型特化,运行性能与手写特定类型代码相当
泛型结构体示例
package main
import "fmt"
// 泛型栈实现
type Stack[T any] struct {
elements []T
}
// 入栈
func (s *Stack[T]) Push(value T) {
s.elements = append(s.elements, value)
}
// 出栈
func (s *Stack[T]) Pop() (T, bool) {
if len(s.elements) == 0 {
var zero T
return zero, false
}
lastIndex := len(s.elements) - 1
value := s.elements[lastIndex]
s.elements = s.elements[:lastIndex]
return value, true
}
func main() {
// 整数栈
intStack := Stack[int]{}
intStack.Push(1)
intStack.Push(2)
intStack.Push(3)
fmt.Println(intStack.Pop()) // 3 true
// 字符串栈
stringStack := Stack[string]{}
stringStack.Push("hello")
stringStack.Push("world")
fmt.Println(stringStack.Pop()) // world true
}
类型推断
Go 编译器能够自动推断类型参数,无需显式指定。
func main() {
// 编译器自动推断 T 为 int
fmt.Println(Max([]int{1, 2, 3}))
// 显式指定类型(有时需要)
var result int = Max[int]([]int{1, 2, 3})
fmt.Println(result)
}
选择约束时:any 最灵活但功能受限;comparable 支持相等比较;联合约束可限制具体类型范围