Appearance
反射
- 反射(Reflection)是一种能够在程序运行时动态检查、访问和修改变量类型与值的机制
- 它允许程序在编译期未知类型信息的情况下,对变量进行操作,常用于处理 JSON 序列化、ORM 框架、依赖注入等场景。
核心概念
reflect.Type:表示变量的类型信息,例如int、string、struct等。reflect.Value:表示变量的值信息,例如10、"hello"、{Name: "张三", Age: 18}等。
操作反射值
获取反射值的类型和值
go
func main() {
x := 42
t := reflect.TypeOf(x) // 获取类型信息(int)
v := reflect.ValueOf(x) // 获取值信息(42)
fmt.Println("Type:", t) // 输出:Type: int
fmt.Println("Value:", v) // 输出:Value: 42
}从反射对象还原为普通变量
go
func main() {
x := 42
v := reflect.ValueOf(x) // 获取值信息(42)
// 还原为 interface{},再断言为 int
val := v.Interface().(int)
fmt.Println(val) // 输出:42
}修改反射值
- 要修改反射值,需满足两个条件:
- 反射对象必须是可寻址的(即变量本身,而非副本)。
- 变量必须是可导出的(如结构体字段首字母大写)。
go
func main() {
x := 42
// 传入指针,再通过 Elem() 解引用获取可修改的反射值
v := reflect.ValueOf(&x).Elem()
// 修改反射值为 100
v.SetInt(100)
fmt.Println(v.Interface().(int)) // 输出:100
}常用方法
反射方法
| 方法 | 描述 |
|---|---|
reflect.TypeOf() | 获取变量的类型信息(reflect.Type) |
reflect.ValueOf() | 获取变量的值信息(reflect.Value) |
reflect.Type 方法
| 方法 | 描述 |
|---|---|
reflect.Type.Name() | 获取类型的名称(如 "int"、"string") |
reflect.Type.Kind() | 获取类型的类别(如 reflect.Int、reflect.String) |
reflect.Type.Size() | 返回该类型变量在内存中的字节大小 |
reflect.Type.Elem() | 返回其指向的元素类型(如 *int 的 Elem () 是 int) |
reflect.Type.NumField() | 若类型是结构体,返回字段数量(仅对结构体有效) |
reflect.Type.Field(i) | 若类型是结构体,返回第 i 个字段的信息(reflect.StructField) |
reflect.Type.Field(i).Name | 若类型是结构体,返回第 i 个字段的名称(如 "Name"、"Age") |
reflect.Type.Field(i).Tag | 若类型是结构体,返回第 i 个字段的标签(如 json:"name") |
reflect.Type.NumMethod() | 若类型是结构体,返回方法数量(仅对结构体有效) |
reflect.Type.Method(i) | 若类型是结构体,返回第 i 个方法的信息(reflect.Method) |
reflect.Type.Field(i).Type | 若类型是结构体,返回第 i 个方法的类型(如 string、int) |
reflect.Value 方法
| 方法 | 描述 |
|---|---|
reflect.Value.Kind() | 返回值的类型 |
reflect.Value.Type() | 返回值对应的 reflect.Type 类型信息 |
reflect.Value.Interface() | 将反射值转换为 interface{} 类型,可通过类型断言还原为原始类型 |
reflect.Value.IsValid() | 判断反射值是否有效(如对 nil 指针调用 Elem() 会返回无效值) |
reflect.Value.IsNil() | 判断值是否为 nil(仅对指针、切片、映射、通道、接口、函数类型有效) |
reflect.Value.Elem() | 返回其指向的元素值(如 *int 的 Elem () 是 int 值) |
reflect.Value.SetXxx() | 修改反射值;如 SetInt()、SetString()、SetFloat() 等,需匹配值类型 |
reflect.Value.NumMethod() | 返回值的方法数量 |
reflect.Value.Method(i int) | 返回第 i 个方法的 reflect.Value(可调用) |
reflect.Value.Call() | 调用反射值的方法;如 Call() 方法,需传入 []reflect.Value{} 类型的参数 |
反射与结构体
- 反射常用于处理结构体,可动态访问其字段和方法
go
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func (u User) Hello() string {
return "Hello, " + u.Name
}
func main() {
u := User{Name: "Alice", Age: 30}
t := reflect.TypeOf(u)
v := reflect.ValueOf(u)
// 1. 获取结构体字段信息
fmt.Println("结构体名称:", t.Name()) // 输出:User
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("字段 %d:名称=%s,类型=%s,标签=%s\n",
i, field.Name, field.Type, field.Tag.Get("json"))
}
// 2. 调用结构体方法
method := v.MethodByName("Hello")
if method.IsValid() {
// 调用无参方法(参数为 []reflect.Value{})
result := method.Call([]reflect.Value{})
fmt.Println(result[0].String()) // 输出:Hello, Alice
}
}注意事项
反射操作绕过了编译期类型检查,运行时开销比直接操作变量高,频繁使用可能影响性能。
反射操作可能因类型不匹配导致 panic(如对 int 调用 SetString()),需通过 IsValid()、Kind() 等方法提前检查。
go
v := reflect.ValueOf(42)
if v.Kind() == reflect.Int { // 检查类型
v.SetInt(100)
}