反射创建对象必须传入可寻址类型:reflect.New和reflect.Zero返回指针或零值的reflect.Value,需.Elem().Interface()取值,但非指针或未导出字段调用.Elem()会panic;仅导出字段可Set;泛型需实例化后反射;性能差且绕过编译检查。
Go 的 reflect.New 和 reflect.Zero 都不能直接返回「值类型实例」,它们返回的是指针。如果你写 reflect.New(reflect.TypeOf(123)),得到的是 *int 类型的 reflect.Value,不是 int 本身。想拿到实际值,得调用 .Elem().Interface();但若类型不可寻址(比如未导出字段的 struct),.Elem() 会 panic。
常见错误现象:panic: reflect: call of reflect.Value.Elem on int Value —— 这是因为对非指针类型的 reflect.Value 调用了 .Elem()。
reflect.New(t) 返回 *T 的指针包装,t 必须是 reflect.Type(不能是 reflect.Value)reflect.Zero(t) 返回 T 类型的零值,但仍是 reflect.Value,需 .Interface() 才能转回 Go 值reflect.TypeOf((*YourType)(nil)).Elem() 获取底层类型即使你用 reflect.New 创建了 struct 指针,只要字段名小写(未导出),调用 .Field(i).Set() 就会 panic:reflect: cannot set unexported field。这不是权限问题,是 Go 反射的硬性限制 —— 它不绕过语言可见性规则。
使用场景:动态加载配置并填充 struct 时,若结构体定义在第三方包里且含私有字段,反射赋值会失败,只能靠 JSON/YAML 解码器(它们内部用 unsafe 绕过,但对外不暴露该能力)。
.Set* 系列方法修改reflect.ValueOf(&s).Elem().FieldByName("Name").CanSet() 返回 false 表示不可设,别跳过这个检查setAccessible(true)
Go 1.18+ 的泛型在运行时被擦除,reflect.TypeOf[MyType[int]] 是非法语法,编译不过。reflect 包完全不知道泛型参数 —— 它只看到实例化后的具体类型,比如 MyType[int] 在反射中就是某个具名或匿名 struct/ptr 类型。
所以「动态创建 map[string]T」这种需求,必须提前知道 T 是什么类型(比如从字符串解析为 "int" 后查表映射到 reflect.TypeOf(0)),再构造 reflect.MapOf(reflect.TypeOf(""), t)。
func makeMap(keyType, valueType reflect.Type) interface{} {
m := reflect.MakeMap(reflect.MapOf(keyType, valueType))
return m.Interface()
}
// 用法:
m := makeMap(reflect.TypeOf(""), reflect.TypeOf(0)) // map[string]int
reflect.MapOf、reflect.SliceOf、reflect.ChanOf 都要求传入已确定的 reflect.Type
reflect.GenericOf 或类似机制reflect.TypeOf(t))操作,不能“推导”类型参数每次 reflect.TypeOf(x) 或 reflect.ValueOf(x) 都触发接口转换和类型查找,比直接 new 快不了多少;而 reflect.New(t)

&T{} 慢 5–10 倍以上(实测)。更关键的是,它绕过了编译器类型检查,错误会在运行时爆发。
容易被忽略的地方:反射创建的对象如果包含 sync.Mutex 或其他非拷贝类型字段,直接 .Interface() 返回后,若被多次复制(如传参、赋值),会导致数据竞争 —— 因为 sync.Mutex 不可拷贝,但反射不阻止你干这事。
unsafe.Sizeof + 池化(sync.Pool)比反复反射创建更高效go:generate)而非运行时反射