

在泛型出现之前,我们只有两个选择:
泛型允许你在定义函数或结构体时,先不指定具体类型,而是用一个占位符(通常叫 T)代替。等真正使用时,再告诉它 T 是什么。
唐伯虎决定先打造一个“万能展示台”,可以展示任何东西。
package main
import "fmt"
// [T any] 是类型参数列表
// T 是占位符名字,随便起 (通常用 T, K, V)
// any 是约束,表示 T 可以是任何类型
func PrintGift[T any](gift T) {
fmt.Printf("华安献上礼物: %v\n", gift)
}
func main() {
// T 自动推导为 string
PrintGift("《唐寅诗集》")
// T 自动推导为 int
PrintGift(9527)
// 显式指定 T 为 bool
PrintGift[bool](true)
}
接下来是重头戏:定义一个能装万物的盒子结构体。
// 定义一个泛型结构体 Box
type Box[T any] struct {
Name string
Content T // Content 的类型取决于 T
}
// 给泛型结构体添加方法
func (b *Box[T]) Open() {
fmt.Printf("打开 %s,里面是: %v\n", b.Name, b.Content)
}
func main() {
// 创建一个装字符串的盒子
bookBox := Box[string]{
Name: "书画盒",
Content: "百鸟朝凤图",
}
bookBox.Open()
// 创建一个装整数的盒子
moneyBox := Box[int]{
Name: "钱箱",
Content: 10000,
}
moneyBox.Open()
}

有时候我们不能让 T 是任意类型 (any)。比如,我们想写一个比较大小的函数,T 就必须支持 > 或 < 操作。
Go 内置了一个 comparable 约束,表示类型支持 == 和 !=。
// 只有支持判等的类型才能传进来
func IsSameGift[T comparable](a, b T) bool {
return a == b
}
func main() {
fmt.Println(IsSameGift(100, 100)) // true
fmt.Println(IsSameGift("香", "臭")) // false
// fmt.Println(IsSameGift([]int{1}, []int{1})) // ❌ 报错!切片不支持 == 比较
}
如果你想限制 T 只能是数字(支持加减乘除),可以自定义接口约束。
// 定义一个约束:只能是 int 或 float64
type Number interface {
int | float64 | int64
}
// 使用自定义约束
func Add[T Number](a, b T) T {
return a + b
}
func main() {
fmt.Println(Add(1, 2)) // 3
fmt.Println(Add(1.5, 2.5)) // 4.0
// fmt.Println(Add("A", "B")) // ❌ 报错!string 不在 Number 约束里
}
如果我定义了一个新类型 type Age int,它还能用上面的 Number 约束吗?
不能!除非你在约束里加个波浪号 ~。
type MyInt int
// ~int 表示:所有底层类型是 int 的类型都能用
type Integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
func Max[T Integer](a, b T) T {
if a > b {
return a
}
return b
}
func main() {
var a, b MyInt = 10, 20
fmt.Println(Max(a, b)) // ✅ 可以用了!
}
唐伯虎想把一串珠子(切片)倒过来串。请写一个泛型函数 Reverse,能反转任何类型的切片。
package main
import "fmt"
// 填空:定义泛型函数
func Reverse[T ____](s []T) []T {
l := len(s)
result := make([]T, l)
for i, v := range s {
result[l-1-i] = v
}
return result
}
func main() {
nums := []int{1, 2, 3}
fmt.Println(Reverse(nums)) // [3 2 1]
strs := []string{"秋", "香", "姐"}
fmt.Println(Reverse(strs)) // [姐 香 秋]
}
任务: 填空,使函数能接收任何类型。
答案: any
func Reverse[T any](s []T) []T { ... }
解析: 反转操作不需要比较,也不需要计算,只需要移动位置,所以 any 类型就足够了。