reflect.TypeOf(x).Kind() 返回底层类别枚举值,如 reflect.Int、reflect.Struct、reflect.Ptr 等共27种固定常量,它忽略类型名称与包路径,仅标识运行时基础形态;例如 *User 的 Kind 是 Ptr,[]int 是 Slice,type MyInt int 的 Kind 仍是 Int。

reflect.TypeOf(x).Kind() 返回什么?
返回的是底层类别枚举值,比如 reflect.Int、reflect.Struct、reflect.Ptr 等共 27 种固定常量。它不关心你叫 UserID 还是 int,只看运行时“算哪一类”。
常见错误现象:t.Kind() == reflect.Struct 写成 t.String() == "struct" 或 strings.Contains(t.String(), "struct")——t.String() 输出带包路径的完整类型名(如 *main.User),不稳定且易误匹配。
- 判断是否可遍历字段:用
v.Kind() == reflect.Struct,不是v.Type().Name() == "User" - 判断是否为切片:用
v.Kind() == reflect.Slice,不是v.Type().String()里找[] - 指针解引用前必须先判
v.Kind() == reflect.Ptr,否则v.Elem()panic
reflect.TypeOf(x) 返回的 Type 到底包含哪些信息?
reflect.Type 是完整类型描述,含包路径、名字、方法集、字段列表等元数据。它的 Name() 只对包级具名类型(如 type User struct{})非空;匿名结构体、函数、接口、指针的 Name() 都是空字符串。
常见错误现象:对 var x *User 调用 reflect.TypeOf(x).Name() 得到空串,误以为“没类型”——其实 Type 对象本身有效,只是名字为空。
立即学习“go语言免费学习笔记(深入)”;
- 要获取结构体字段名:必须用
t.Field(i).Name,Kind没有字段信息 - 要精确识别自定义错误类型:
t == reflect.TypeOf(&MyError{}),不能只靠Kind == reflect.Ptr - 要检查方法是否存在:
t.MethodByName("Save"),Kind不提供方法表
为什么反射逻辑里多数分支该用 Kind 而不是 Type?
因为 Kind 稳定、轻量、穿透间接性。比如 type MyInt int 和原生 int 的 Kind 都是 reflect.Int,序列化时你想统一当整数处理——这正是 Kind 的设计意图。
而 Type 相等是严格全等:包路径、名字、定义方式全一致才算相等。写泛型逻辑时若用 Type 分支,type A int 和 type B int 就会走不同分支,违背“统一处理”的初衷。
- 解包多层指针:
for v.Kind() == reflect.Ptr { v = v.Elem() }——Kind让你安全地“往下钻” - 接口值取实际类型:
v.Kind() == reflect.Interface && !v.IsNil()后再用v.Elem().Kind() - 避免
panic: reflect: call of reflect.Value.Kind on zero Value:先v.IsValid(),再取Kind
什么时候必须用 Type 做精确匹配?
当你需要区分“同 Kind 不同语义”的类型时,比如只允许 *main.User,拒绝其他任何 reflect.Ptr;或只处理带特定 tag 的结构体字段,就得靠 t.Field(i).Tag——这些都依赖 Type 提供的完整元数据。
容易被忽略的点:reflect.TypeOf(nil) 返回 nil,此时调 .Kind() 直接 panic;但 reflect.ValueOf(nil) 返回一个 IsValid() == false 的 Value,必须先守 IsValid() 才能继续。
- 判断是否为某个具体指针类型:
t == reflect.TypeOf((*User)(nil)).Elem(),不是t.Kind() == reflect.Ptr - 获取字段 JSON tag:
t.Field(i).Tag.Get("json"),Kind不存 tag 信息 - 生成零值:
reflect.Zero(t)参数必须是Type,不是Kind


















