反射
应用场景
- 序列化与反序列化
- ORM 框架
- 依赖注入(DI)框架
- 动态函数/方法调用
Go语言反射机制要点
Go语言的反射机制主要有以下特点:
-
动态类型检查 - 允许程序在运行时获取变量的类型(Type)、类别(Kind)和值(Value)信息
-
结构体分析 - 对于结构体类型,可以进一步获取其包含的字段和方法等详细信息
-
动态操作 - 支持在运行时修改变量的值以及调用与之关联的方法
-
使用方式 - 需要导入标准库中的"reflect"包来实现反射功能
这些特性使得Go程序能够在运行时动态地检查和操作各种类型的变量,为编写更灵活的代码提供了可能。
重要函数和机制
-
基础反射函数
- reflect.TypeOf(变量名)
功能:获取变量的类型信息
返回值:reflect.Type类型
用途:用于分析变量的静态类型信息 - reflect.ValueOf(变量名)
功能:获取变量的值信息
返回值:reflect.Value类型(结构体类型)
用途:通过reflect.Value可以获取变量的详细信息,如字段值、方法等
- reflect.TypeOf(变量名)
-
类型转换关系
变量、interface{}和reflect.Value三者之间可以相互转换:
变量 → interface{} → reflect.Value
reflect.Value → interface{} → 变量
这种转换在实际开发中经常使用,特别是在需要处理未知类型数据时
代码示例:
// 反射类型转换示例函数
func reflectConvert(b interface{}) {
// 第一步:interface{} → reflect.Value
rVal := reflect.ValueOf(b)
// 第二步:reflect.Value → interface{}
iVal := rVal.Interface()
// 第三步:interface{} → 原类型(使用类型断言)
// 假设原类型为Stu结构体
v := iVal.(Stu)
}
为何要以interface{}类型传入呢,不直接以结构体传入?这种情况的出现自然有缘由。
如果指定某个结构体类型,那么这个函数就只适用于这一个结构体了,如果使用接口,那只需要传入的结构体实现了这个接口,就可以作为参数传入,使代码量减少,维护性增强
快速入门
- 对基本数据类型进行反射
代码示例:
func Change(b interface{}) {
rType := reflect.TypeOf(b)
rValue := reflect.ValueOf(b)
fmt.Printf("%v,%v,%T,%v\n", rType, rValue, b, b)
fmt.Println(2 + b.(int))
fmt.Println(2 + rValue.Int())
}
func main() {
a := 10
Change(a)
}
输出结果:
int,10,int,10
12
12
注意点:虽然rValue的输出是10,但是2+rValue是跑不通的,因为reflect包实现了运行时反射,但是在编译时,还是处于静态,rValue还是被当作成一个refelct.interface类型,不同类型之间不能相加
注意事项
- reflect.Value.Kind()的作用
该函数用于获取一个变量的"类别(Kind)",其返回值是一个预定义的常量(例如 Int, String等)。使用者需要查阅官方手册来了解所有可能的常量值。 - "类型(Type)"与"类别(Kind)"的区别与联系
- Type 指变量明确的类型。
- Kind 指变量底层的基本类别。
- 两者可能相同,也可能不同。
- 举例来说:
-
变量 num int:其 Type 是 int,Kind 也是 int(此时相同)。
-
变量 stu Student(一个自定义结构体):其 Type 是 包名.Student,而 Kind 是 struct(此时不同)。
-
- 使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int类型,那么就应该使用reflect.Value(x).Int(),而不能使用其它的,否则报panic。如果是结构体,就用类型断言。
- 通过反射修改内容时,需要传入的是地址
代码示例:
func reflects(b interface{}) {
rValue := reflect.ValueOf(b)
fmt.Println(rValue.Elem())
rValue.Elem().SetInt(21)
fmt.Println(rValue.Elem())
}
func main() {
num := 2
reflects(&num)
}
输出结果
2
21
其中,rValue.Elem()类似于rValue,但是由于rValue是reflect.Value类型,所以不能使用rValue
- Method()函数,用于获得某个方法,此时对于方法的排序是根据方法的字母ASCII由小到大排列