什么是反射
简单来说, 反射就是: 在程序运行时, 动态地获取变量的类型信息和结构信息, 并能动态调用方法、修改属性
推荐阅读:
反射的三大法则
- 从接口值到反射对象: 使用
reflect.TypeOf()和reflect.ValueOf() - 从反射对象到接口值: 使用
reflect.Value.Interface()方法 - 要修改反射对象, 其值必须是可设置的(Settable): 这意味着你通常需要传递指针而不是值
go
package main
import (
"fmt"
"reflect"
)
func reflectType(x interface{}) {
v := reflect.TypeOf(x)
fmt.Printf("type:%v\n", v)
}
func main() {
var a float32 = 3.14
reflectType(a) // type:float32
var b int64 = 100
reflectType(b) // type:int64
}反射的优缺点
由于缺点非常明显, 不到必要, 不要滥用
优点
- 通用性 :可以编写处理任意类型的函数(如通用的 Print、Clone、DeepEqual)
- 框架开发:是 ORM (Gorm)、Web 框架 (Gin)、序列化 (JSON) 的基石
- 动态性 :可以在运行时动态决定调用哪个方法或访问哪个字段
缺点
- 性能开销 : 反射涉及动态类型查找,比直接代码慢很多(通常慢 10-100 倍)
- 类型安全丢失: 编译期无法检查错误,错误会推迟到运行时 panic
- 代码难读 : 反射代码通常晦涩难懂,维护成本高
- 不可导出字段: 反射无法修改小写字母开头的私有字段(会 panic)
go
package main
import (
"fmt"
"reflect"
"strings"
)
type User struct {
ID int `json:"userId"`
Name string `json:"userName"`
Email string `json:"email"`
Password string `json:"password"`
}
func main() {
mapData := StructToMap(User{
ID: 1001,
Name: "secret",
Email: "secret@example.com",
Password: "123456",
})
fmt.Println(mapData)
}
func StructToMap(obj interface{}) map[string]interface{} {
result := make(map[string]interface{})
v := reflect.ValueOf(obj)
t := reflect.TypeOf(obj)
// 如果是指针,获取其指向的元素
if v.Kind() == reflect.Ptr {
v = v.Elem()
t = t.Elem()
}
if v.Kind() != reflect.Struct {
return result
}
for i := 0; i < v.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
// 跳过未导出的字段
if !value.CanInterface() {
continue
}
// 优先使用 json tag,如果没有则使用字段名
tag := field.Tag.Get("json")
if tag == "" || tag == "-" {
tag = field.Name
} else {
// 处理 json:"name,omitempty" 中的逗号
if idx := strings.Index(tag, ","); idx != -1 {
tag = tag[:idx]
}
}
result[tag] = value.Interface()
}
return result
}