

新闻资讯
技术教程reflect.Type 用于运行时检查具体值的类型信息,不能反推未声明类型或动态创建类型;核心入口是 reflect.TypeOf() 和 reflect.ValueOf().Type(),前者更轻量但不可传 nil 解引用。
Go 语言的 reflect.Type 不是用来“获取类型”的工具,而是用来**在运行时检查已知值的类型信息**。你无法用反射“反推”一个未声明、未赋值、未传入的类型;它必须基于一个具体值(interface{} 或具体变量)才能拿到 Type。
核心入口只有两个函数:reflect.TypeOf() 和 reflect.ValueOf().Type()。前者更常用、更轻量。
reflect.TypeOf() 接收任意值,返回 reflect.Type;它底层会自动把参数转成 interface{},所以不能传 nil 指针的解引用(如 *T(nil) 会 panic)reflect.ValueOf(x).Type() 多一步封装,适合后续还要用 Value 做操作的场景,但有额外开销reflect.TypeOf(nil) 返回 nil,不是某个类型的指针类型——因为 nil 本身没有类型上下文package main
import (
"fmt"
"reflect"
)
func main() {
s := "hello"
fmt.Println(reflect.TypeOf(s)) // string
fmt.Println(reflect.TypeOf(&s)) // *string
fmt.Println(reflect.TypeOf((*int)(nil))) // *int(需显式类型转换)
}
reflect.Type 是只读的类型描述符,**不能创建新类型、不能修改字段顺序、不能生成 struct 定义**。它反映的是编译期已确定的类型结构。
reflect.StructField + reflect.StructTag 去读已有 struct 的布局Type 是否“语义等价”?别依赖 ==,要用 t1.AssignableTo(t2) 或 t1.ConvertibleTo(t2),因为别名类型(type MyInt int)和原类型 int 的 Type 对象不相等reflect.TypeOf(0).Name() 对基础类型返回空字符串,只有命名类型(如自定义 struct、type 别名)才返回非空名对 struct 类型调用 t.NumField() 后,用 t.Field(i) 拿到 StructField,它的字段含义常被误解:
Name:仅当字段是导出(大写开头)时才有值;非导出字段返回空字符串,但依然可被 Field() 访问(只要原始值可寻址)PkgPath:非空表示该字段来自其他包且是非导出的(即 embed 了未导出 struct),此时反射无法访问其内部字段Anonymous:true 表示是匿名字段(内嵌),但要注意:如果内嵌的是指针类型(如 *http.Client),Anonymous 仍是 true,但字段本身不可直接取地址,需先 Elem()
type User struct {
Name string `json:"name"`
age int `json:"-"` // 非导出,Name == ""
}
t := reflect.TypeOf(User{})
f := t.Field(1)
fmt.Println(f.Name) // ""
fmt.Println(
f.PkgPath) // ""(本包内定义)
fmt.Println(f.Anonymous) // false
反射是运行时开销明确的操作,reflect.TypeOf() 虽比 ValueOf() 快,但仍涉及接口转换和类型断言。高频路径(如 HTTP 中间件、序列化 hot path)应避免。
reflect.Type 结果(它是可比较、可 map key 的),别每次调 reflect.TypeOf(x)
reflect.Type 安全,但它不包含任何状态,只是只读视图;真正危险的是 reflect.Value(尤其 Set 系列方法)最易忽略的一点:reflect.Type 对 interface 类型返回的是其**动态类型**,不是 interface 本身。比如 var x interface{} = "hi",reflect.TypeOf(x) 是 string,不是 interface{} —— 这一点在写通用序列化逻辑时经常导致误判类型分支。