Go 泛型的约束与类型集合怎么理解?和接口怎么取舍?

一句话结论:泛型约束本质是“类型集合”:可以是接口(方法集),也可以是类型集(~ 底层类型、| 联合类型),函数或类型带类型参数后由编译器在调用处实例化;泛型是编译期的“类型参数化”,接口是运行期的“行为抽象”——同构算法选泛型,需要多实现多态选接口。

类型集合怎么读

  • any = interface{}:所有类型组成的集合。
  • comparable:可用 == / != 比较的类型集合,内建约束。
  • interface{ ~int | ~string }:底层类型是 int 或 string 的类型。
  • 自定义约束:type Number interface { ~int | float64 },在约束中“|”表示并集、“”表示含底层类型。

泛型 vs 接口

维度泛型接口
生效时机编译期实例化(单态化)运行期动态分派
类型安全强,调用时即确定可能需断言
性能无装箱,通常更快有动态分派与潜在装箱
灵活性类型需编译期确定实现可在运行期替换
适用场景容器、算法、统一处理类型结构多实现、插件、依赖注入、测试替身

口诀:能用接口表达清楚“行为”就先上接口;只有“同一套算法要套用到多种具体类型”时才值得用泛型。泛型还会让编译更慢、错误信息更绕。

代码示例

// 类型集合约束:可比较大小的一类数值
type Number interface {
	~int | ~int64 | ~float64
}

func Min[T Number](a, b T) T {
	if a < b {
		return a
	}
	return b
}

fmt.Println(Min[int](3, 5))
fmt.Println(Min(1.5, 2.5)) // 类型推断

常见追问 / 记忆点

  • 追问:~int 和 int 作约束有何区别?——~int 允许底层类型是 int 的自定义类型,int 只允许字面 int 本身。
  • 追问:为什么接口也能当约束?——约束就是类型集合,方法集与类型集都是集合,语法上统一用接口表达。
  • 记忆点:泛型 = 编译期模板化,零运行时开销但增加编译负担;接口 = 运行期多态。先用接口,必要时泛型。
笔记加载中…