反射
是什么?
- 在编译时并不知道这些变量的具体类型。
- 在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
- 就是把类型元数据暴露给用户使用。
应用
- 对象序列化(encoding/json)
- fmt 相关函数的实现
- ORM
- 依赖注入
如何实现反射
Go语言反射是建立在Go类型系统和interface设计之上的。 在 reflect 包里定义了各种类型,实现了反射的各种函数,通过它们可以在运行时检测类型的信息、改变类型的值。
关键功能
核心功能是把interface变量转化为反射类型对象reflect.Type和reflect.Value,并通过反射类型对象提供的功能,访问真实对象的方法和属性。
type any = interface{}
// 获取反射对象reflect.Type
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i any) Type
// 获取反射对象reflect.Value
// ValueOf returns a new Value initialized to the concrete value stored in the interface i.
// ValueOf(nil) returns the zero Value.
func ValueOf(i any) Value
// 反射对象转换回interface
func (v Value) Interface() (i any)
好处
- 提升代码灵活性:不知道接口调用哪个函数.
- 配置化
- 插件化
- 代码解耦
- 依赖注入
坏处
- 代码可读性差。
- 静态语言,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接 panic,可能会造成严重的后果。
- 反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。
DeepEqual 的实现只需要递归地调用 == 就可以比较两个变量是否是真的“深度”相等。
有一些异常情况:比如 func 类型是不可比较的类型,只有在两个 func 类型都是 nil 的情况下,才是“深度”相等;float 类型,由于精度的原因,也是不能使用 == 比较的;包含 func 类型或者 float 类型的 struct, interface, array 等。
1. 功能区别
函数 | 作用 | 返回值类型 |
---|---|---|
reflect.TypeOf(obj) |
获取 obj 的类型信息(如结构体名称、方法、字段等) |
reflect.Type |
reflect.ValueOf(obj) |
获取 obj 的值信息(如实际存储的数据),并支持动态操作值(修改、调用方法) |
reflect.Value |
2. 返回对象的能力
reflect.Type
(通过 TypeOf
获取)
- 提供类型元信息:
- 类型名称(
Name()
) - 类型种类(
Kind()
,如int
、struct
、slice
) - 方法列表(
NumMethod()
、Method(i)
) - 结构体字段信息(
NumField()
、Field(i)
) - 是否实现某接口(
Implements(u Type)
)
- 类型名称(
- 特性:
仅用于读取类型信息,不可修改数据。
reflect.Value
(通过 ValueOf
获取)
- 提供值操作能力:
- 获取实际值(
Interface()
) - 修改值(
Set()
、SetInt()
,需满足可寻址性) - 调用方法(
Call()
) - 判断是否零值(
IsZero()
)
- 获取实际值(
- 可寻址性要求:
必须通过指针获取Value
才能修改值(例如Value.Elem()
)。
关键区别总结
| 维度 | reflect.Type
| reflect.Value
|
|—————-|———————————————|———————————————-|
| 核心功能 | 类型元数据分析 | 值操作与动态行为 |
| 数据修改 | ❌ 不可修改 | ✅ 可修改(需可寻址) |
| 典型应用 | 检查结构体字段、接口实现 | 动态调用方法、序列化/反序列化、依赖注入 |