Go 中 interface{} 和 any 怎么选?
一句话总结
any是interface{}的官方别名(Go 1.18+),两者完全等价。新代码用any,老代码不用主动改。
go
// 标准库源码(builtin/builtin.go)
type any = interface{}
注意是 类型别名 (=),不是新类型。编译后字节码完全一样,零成本。
一、为什么 Go 1.18 要加 any?
Go 1.18 引入了泛型,类型约束写起来太丑:
go
// ❌ 老写法
func Print[T interface{}](v T) { ... }
// ✅ 新写法
func Print[T any](v T) { ... }
为了让泛型约束更可读,官方加了 any 这个内置别名,顺手也用在了普通空接口场景。
二、两者的区别
| 维度 | interface{} |
any |
|---|---|---|
| 引入版本 | Go 1.0 | Go 1.18+ |
| 本质 | 空接口类型 | interface{} 的别名 |
| 运行时性能 | 完全相同 | 完全相同 |
| 可读性 | 略啰嗦 | 简洁 |
| 标准库使用 | 老代码 | 1.18+ 已全面替换 |
| 工具推荐 | --- | gopls、golangci-lint 推荐 |
go
var a any = 1
var b interface{} = 1
// 完全可以互相赋值,类型断言、反射结果一样
a = b
b = a
三、怎么选?
✅ 用 any 的场景(推荐 99%)
-
新项目、新代码
gofunc Log(v ...any) { ... } -
泛型约束
gofunc Map[K comparable, V any](m map[K]V) []V { ... } -
变长参数
gofunc Printf(format string, args ...any) { ... } -
map / slice 装混合类型
gom := map[string]any{ "name": "alice", "age": 18, }
⚠️ 保留 interface{} 的少数场景
- 要兼容 Go 1.17 及以下版本(极少见,1.18 已发布多年)
- 改动会污染大量历史代码 / 增加无意义 diff(团队规约)
- 强调"这是一个空接口类型" 而非泛型类型参数
- 公开 API 已经用了
interface{},为了不破坏向后兼容继续保留
四、批量替换工具
Go 官方提供了一键替换工具:
bash
gofmt -r 'interface{} -> any' -w .
或者用 gopls:
bash
gopls codeaction -kind=refactor.rewrite ...
golangci-lint 启用 revive 规则后也会自动提示。
五、常见误区
误区 1:以为 any 是新类型
go
var a any
var b interface{}
// a 和 b 是 同一个类型,不是子类型关系
fmt.Printf("%T %T\n", a, b) // <nil> <nil>
误区 2:以为 any 性能更好
❌ 没有任何区别。底层都是 (type, value) 双指针结构(eface)。
误区 3:以为可以省掉类型断言
不行,从 any 取出具体类型还是要断言:
go
var v any = 42
n := v.(int) // 类型断言
m, ok := v.(string) // 安全断言
误区 4:以为泛型里 any 等于 comparable
不对:
any:任何类型(不能用==比较)comparable:可比较类型(能用==)
go
func Eq[T any](a, b T) bool {
return a == b // ❌ 报错,any 不一定可比较
}
func Eq[T comparable](a, b T) bool {
return a == b // ✅
}
六、实战示例
JSON 解析
go
var data map[string]any
json.Unmarshal(raw, &data)
if name, ok := data["name"].(string); ok {
fmt.Println(name)
}
泛型容器
go
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
函数选项模式
go
type Option func(*Config)
func WithValue(k string, v any) Option {
return func(c *Config) { c.values[k] = v }
}
七、一句话记忆
any=interface{},新代码统一写any,老代码不用动,性能零差异。 唯一要注意:泛型里any≠comparable,需要==比较时用comparable。