Go反射需显式判断Kind而非Name,Interface()前须检查IsValid()和CanInterface(),结构体遍历时用PkgPath()==""过滤非导出字段,高频场景应避免反射改用代码生成。

Go 的反射不支持“泛型式自动适配”,必须显式判断 reflect.Kind 或 reflect.Type,否则会 panic 或行为异常。
如何用 reflect.Value.Interface() 安全取出原始值
直接调用 reflect.Value.Interface() 前,必须确保该值是可导出的(即字段首字母大写),且不是未初始化的零值指针。否则会 panic:reflect: call of reflect.Value.Interface on zero Value。
- 先用
v.IsValid()检查是否为有效值 - 再用
v.CanInterface()判断能否安全转为 interface{} - 对结构体字段,还需确认其是否导出(
v.CanAddr() && v.CanInterface()通常更稳妥) - 若处理的是指针类型,建议先
v.Elem()解引用,再检查有效性
为什么 reflect.Kind() 比 reflect.Type.Name() 更可靠
reflect.Type.Name() 只返回命名类型的名称(如 "string"、"MyStruct"),对匿名类型(如 struct{X int}、[]string、map[string]int)返回空字符串;而 reflect.Kind() 统一返回底层类别(reflect.Struct、reflect.Slice、reflect.Map 等),适用于所有类型分支判断。
- 判断切片:用
v.Kind() == reflect.Slice,而非v.Type().Name() == "slice"(后者永远不成立) - 判断基础类型:优先用
v.Kind() >= reflect.Int && v.Kind() 覆盖全部数值类 - 自定义类型(如
type UserID int64)的Kind()仍是reflect.Int64,适合统一数值处理逻辑
遍历 struct 字段时如何跳过非导出字段并保留顺序
Go 反射中 reflect.StructField 的顺序与源码定义一致,但 reflect.Value.NumField() 和 reflect.Value.Field(i) 默认包含所有字段(含小写字段)。若需只处理可导出字段,必须手动过滤。
立即学习“go语言免费学习笔记(深入)”;
- 用
field.Type.PkgPath() == ""判断是否导出(导出字段的PkgPath为空字符串) - 不要依赖
strings.ToUpper(field.Name[:1]) == field.Name[:1]—— 这在 Unicode 标识符下不可靠 - 若需 JSON tag 映射,用
field.Tag.Get("json"),注意空 tag 返回空字符串,需额外判空 - 字段名大小写敏感:反射拿到的
field.Name是原始名,不会自动转成 JSON key 的 snake_case
反射开销明显,且无法被编译器优化;高频路径(如 HTTP 请求绑定、数据库 scan)应优先考虑代码生成(go:generate + structtag)或预编译类型处理器,而不是运行时反复 reflect.TypeOf。










