最可靠方式是用 t.Kind() == reflect.Struct 判断结构体,需先处理指针解引用和 nil 值;Name() 不能用于判断是否为结构体,仅表示类型名称。

直接用 t.Kind() == reflect.Struct 判断是否为结构体
这是最可靠、最常用的方式。Go 的 reflect.Type.Kind() 返回的是底层类型类别,reflect.Struct 就代表“它是一个结构体”,不关心是不是自定义命名、有没有字段、是否为空结构体(如 struct{}),只要语法上是 struct 类型,就返回 Struct。
-
reflect.TypeOf(Person{}).Kind()→reflect.Struct -
reflect.TypeOf(struct{X int}{}).Kind()→ 同样是reflect.Struct,哪怕没名字 -
reflect.TypeOf(&Person{}).Kind()→ 是reflect.Ptr,不是Struct,这点必须注意
传入指针时要先调用 Elem() 解引用
很多实际场景中你拿到的是结构体指针(比如函数参数是 *User 或接口里存了 *Config),直接对指针调 Kind() 会得到 Ptr,误判为“不是结构体”。必须先判断是否为指针,再用 t.Elem() 获取它指向的类型。
- 错误写法:
reflect.TypeOf(&User{}).Kind() == reflect.Struct→false - 正确写法:
t := reflect.TypeOf(&User{}); if t.Kind() == reflect.Ptr { t = t.Elem() }; return t.Kind() == reflect.Struct - 别忘了:如果传进来的是
nil指针(如var u *User),t.Elem()不会 panic,但t本身是nil,调t.Kind()会 panic —— 所以安全起见,先判t != nil
别用 Name() 判断是否为 struct
t.Name() 只对**包内定义的导出命名类型**返回非空字符串,比如 type User struct{} 会返回 "User";但对匿名结构体(struct{})、未导出类型(type user struct{})或内建类型(int、[]string)都返回空字符串。它根本不是用来判断“是不是结构体”的,而是用来识别“叫什么名字”。
-
reflect.TypeOf(struct{X int}{}).Name()→"",但它确实是 struct -
reflect.TypeOf([]int{}).Name()→"",但它不是 struct,是 slice - 混淆
Name()和Kind()是新手最常见的反射误用之一
接口值传入前务必检查是否为 nil
如果你的函数接收的是 interface{},而调用方传了 nil(比如 var x interface{} 直接传进去),reflect.TypeOf(x) 会返回 nil,紧接着调 .Kind() 或 .Name() 就 panic。
立即学习“go语言免费学习笔记(深入)”;
- 安全做法:先用
if x == nil判断,或改用reflect.ValueOf(x),它对nil接口返回一个IsValid() == false的reflect.Value,可安全调.Kind()(返回Invalid) - 更稳妥的通用判断函数长这样:
func isStruct(v interface{}) bool { val := reflect.ValueOf(v) if !val.IsValid() { return false } t := val.Type() if t.Kind() == reflect.Ptr { t = t.Elem() } return t.Kind() == reflect.Struct }
真正容易被忽略的,是 nil 接口和指针解引用这两层边界 —— 它们不会报编译错误,但一跑就 panic,而且往往在边缘 case(比如测试传空、ORM 读不到数据)才暴露。










