Golang如何通过反射获取变量的真实类型

reflect.TypeOf()获取接口底层类型需先判空,返回reflect.Type;指针需.Elem()取元素类型;Kind()判容器类别,Name()仅对命名类型非空;泛型用(*T)(nil).Elem()获取真实类型。

reflect.TypeOf() 获取接口变量的底层类型

Go 的反射中,interface{} 本身不携带具体类型信息,必须用 reflect.TypeOf() 提取其动态类型。注意:它返回的是 reflect.Type,不是字符串或基础类型名。

  • 对未包装的原始值(如 intstring)直接传入,reflect.TypeOf() 返回其真实类型
  • 对指针变量,它返回的是指针类型(如 *int),不是所指元素类型;需调用 .Elem() 才能拿到底层类型
  • nil 接口,reflect.TypeOf() 返回 nil,直接调用会 panic,务必先判空
var x int = 42
t := reflect.TypeOf(x)
fmt.Println(t.Name())     // "int"
fmt.Println(t.Kind())   // reflect.Int

var p *int = &x pt := reflect.TypeOf(p) fmt.Println(pt.Elem().Name()) // "int"

reflect.ValueOf().Kind() 区分基础类别而非具体类型名

Kind() 返回的是 Go 类型系统中的“种类”(如 reflect.Structreflect.Slice),和 Name() 不同——后者只对命名类型(如自定义 struct、type alias)有值,对匿名类型(如 []stringmap[int]bool)返回空字符串。

  • 判断是否为切片、映射、结构体等容器类型时,优先用 Kind()
  • Name() 只在类型有名字(即用 type T xxx 声明过)时非空;例如 type MyInt intName()"MyInt",但 int 本身的 Name() 是空串
  • 需要跨包识别用户定义类型时,PkgPath() 可辅助判断是否来自当前包
type Person struct{ Name string }
var p Person
fmt.Println(reflect.ValueOf(p).Kind()) // struct
fmt.Println(reflect.TypeOf(p).Name())  // "Person"
fmt.Println(reflect.TypeOf(struct{X int}{}).Name()) // ""(匿名 struct)

处理 interface{} 时必须先确认是否为 nil,否则 reflect.TypeOf() panic

reflect.TypeOf() 传入一个值为 nilinterface{} 变量,会导致运行时 panic:“reflect: TypeOf(nil)”。

  • 这不是指指针为 nil,而是指整个接口值为 nil(即 iface.tab == nil && iface.data == nil
  • 安全做法是:先用 if v == nil 判断,或用 reflect.ValueOf(v).Kind() == reflect.Invalid 检测
  • 若已知是接口类型且可能为 nil,建议统一转成 reflect.Value 再判断,避免直接调 TypeOf
var i interface{} = nil
// ❌ panic:
// reflect.TypeOf(i)

// ✅ 安全: v := reflect.ValueOf(i) if v.Kind() == reflect.Invalid { fmt.Println("interface is nil") } else { fmt.Println(v.Type()) }

获取泛型参数的真实类型要用 reflect.TypeOf((*T)(nil)).Elem()

Go 1.18+ 泛型函数中,类型参数 T 在运行时被擦除,无法直接用 reflect.TypeOf(T)(语法错误)。必须通过取地址再解引的方式绕过编译限制。

立即学习“go语言免费学习笔记(深入)”;

  • (*T)(nil) 构造一个指向 T 的空指针类型,reflect.TypeOf 能识别其类型
  • 再调用 .Elem() 得到 T 本身,这才是你要的真实类型
  • 此技巧仅适用于编译期已知的类型参数;若 Tinterface{} 或带约束的类型,仍需结合 ValueOf 运行时值分析
func GetType[T any]() reflect.Type {
    return reflect.TypeOf((*T)(nil)).Elem()
}

type User struct{ ID int } fmt.Println(GetType[User]().Name()) // "User"

类型擦除和接口 nil 判定这两处最容易出错,尤其是泛型场景下漏掉 .Elem() 或对空接口盲目调 TypeOf