interface和any

Go 中 interface{}any 怎么选?

一句话总结

anyinterface{} 的官方别名(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+ 已全面替换
工具推荐 --- goplsgolangci-lint 推荐
go 复制代码
var a any = 1
var b interface{} = 1

// 完全可以互相赋值,类型断言、反射结果一样
a = b
b = a

三、怎么选?

✅ 用 any 的场景(推荐 99%)

  1. 新项目、新代码

    go 复制代码
    func Log(v ...any) { ... }
  2. 泛型约束

    go 复制代码
    func Map[K comparable, V any](m map[K]V) []V { ... }
  3. 变长参数

    go 复制代码
    func Printf(format string, args ...any) { ... }
  4. map / slice 装混合类型

    go 复制代码
    m := map[string]any{
        "name": "alice",
        "age":  18,
    }

⚠️ 保留 interface{} 的少数场景

  1. 要兼容 Go 1.17 及以下版本(极少见,1.18 已发布多年)
  2. 改动会污染大量历史代码 / 增加无意义 diff(团队规约)
  3. 强调"这是一个空接口类型" 而非泛型类型参数
  4. 公开 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,老代码不用动,性能零差异。 唯一要注意:泛型里 anycomparable,需要 == 比较时用 comparable

相关推荐
步行cgn1 小时前
MyBatis Error evaluating expression ‘ids‘. Return value (3) was not iterable 错误详
java·后端
神奇小汤圆1 小时前
Spring Boot + LangChain4j实现RAG——从零搭建企业级知识库问答系统
后端
H_Peak1 小时前
Python运算符与流程控制:if条件判断
后端
H_Peak1 小时前
Python字符串操作:从基础到正则入门
后端
自进化Agent智能体1 小时前
Hermes 子代理委派 —— 并行工作的艺术
后端
步行cgn1 小时前
MyBatis 动态 SQL 中的 <foreach> 标签完全解析
后端
SomeB1oody2 小时前
【RustyML入门】5.0. 模型评估
开发语言·后端·机器学习·rust·教程
唐青枫3 小时前
别只把 enum 当常量列表:Zig 枚举、状态机与 Tagged Union 实战
后端
2601_963870213 小时前
【计算机毕业设计】基于Spring Boot的非遗文创产品交易平台的设计与实现
java·spring boot·后端