结论:反射在运行时检查类型信息(reflect.TypeOf)和读写值(reflect.ValueOf),实现泛型代码:序列化(encoding/json)、ORM、依赖注入、深度拷贝都建立在它上面。代价是性能(装箱、动态派发,比静态代码慢一个数量级以上)和可读性/类型安全(错误推迟到运行时 panic)。
展开:三条基本法则(Rob Pike 的 Reflection Laws):1)接口值可反射为 reflect.Value/Type;2)reflect.Value 可转回接口值(.Interface());3)要修改值,reflect.Value 必须可取地址——reflect.ValueOf(&x).Elem() 得到可设置的 Value,直接 ValueOf(x) 拿到的是副本,Set 会 panic。常用 API 纪律:Kind() 与 Type() 区分(type MyInt int 的 Kind 是 Int);结构体遍历用 NumField+Field(i),tag 用 Tag.Get("json");未导出字段可读不可写(CanSet 为 false,强行操作 panic)。易错点:1)nil 接口反射后 IsValid 为 false,链式调用前先判断;2)反射创建值用 reflect.New(t) / reflect.MakeSlice 而非构造零值乱试;3)热路径上的反射结果(Type、字段索引)应缓存复用。
func setField(v any, name string, val any) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
return errors.New("需要结构体指针")
}
f := rv.Elem().FieldByName(name)
if !f.CanSet() { return errors.New("字段不可写") }
f.Set(reflect.ValueOf(val))
return nil
}
追问方向:Go 1.18 泛型为何仍取代不了反射(运行时类型 vs 编译期类型)、json 反序列化如何用反射填充结构体。