Go 语言泛型
Go 1.18 正式引入泛型(Generics),也叫类型参数。此前想写一个"取切片第一个元素"的函数,int、string、float64 每种类型都得复制一份几乎相同的代码;泛型允许把类型写成参数,调用时才确定,一份代码通吃所有类型。(说明:片段需放入 main 函数运行)
泛型解决什么问题
func firstInt(s []int) int { return s[0] } // 泛型之前:逐类型复制
func first[T any](s []T) T { return s[0] } // 泛型之后:一份通吃
泛型函数语法
在函数名后加方括号声明类型参数:[T any] 表示 T 可以是任意类型,参数与返回值里都可使用 T;同一函数被不同具体类型实例化时,编译器自动生成对应版本。
package main
import "fmt"
func first[T any](s []T) T { // any 即 interface{} 的别名
return s[0]
}
func main() {
fmt.Println(first([]int{1, 2, 3})) // 输出:1
fmt.Println(first([]string{"a", "b"})) // 输出:a
fmt.Println(first([]float64{9.9, 8.8})) // 输出:9.9
}
类型约束:any、comparable 与自定义
约束决定 T 能做什么运算:any 最宽,只能传参、返回;想比较相等要用内置约束 comparable:
func Index[T comparable](list []T, want T) int {
for i, v := range list {
if v == want {
return i
}
}
return -1
}
// 放入 main 调用:
// fmt.Println(Index([]string{"春", "夏"}, "夏")) // 输出:1
想做加法、取大小,得用自定义约束(Go 1.18 起接口可以写类型集合):
type Number interface {
int | int64 | float64 // 只允许这三种类型
}
func Sum[T Number](nums []T) T {
var total T
for _, v := range nums {
total += v // 有约束才能用 + 运算
}
return total
}
// fmt.Println(Sum([]int{1, 2, 3})) // 输出:6
// fmt.Println(Sum([]float64{1.5, 2.5})) // 输出:4
一句话:若自定义类型 type MyInt int 也想匹配 int,约束写成 ~int,表示"底层类型为 int 的所有类型"。
泛型类型:泛型栈示例
结构体等类型定义也能带类型参数,比如支持任意元素类型的栈:
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) { // 方法上也要带 [T]
s.items = append(s.items, v)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T // 用零值返回
return zero, false
}
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return v, true
}
// 放入 main:
// st := Stack[int]{}
// st.Push(10)
// v, ok := st.Pop()
// fmt.Println(v, ok) // 输出:10 true
小结:Go 1.18 引入泛型,用 [T 约束] 消灭重复的类型代码;any 最宽、comparable 可比较,自定义约束用类型集合,~ 表示底层类型。