反射的基本介绍
- 反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
- 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法
- 使用反射,需要
import "reflect"
反射应用场景
- 序列化与反序列化(json的应用场景,结构体指定tag)
- 反射机制编写函数的适配器,桥连接
反射重要函数和概念
- reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型(接口类型,具体看文档)
- reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(结构体类型,具体看文档)
- 变量、interface{} 和 reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。
- 变量 --> interface{}:传递参数,或者强转
- interface{} --> reflect.Value:通过reflect.Value()函数
- reflect.Value --> interface{}: v.interface{},v的类型为reflect.Value
- interface{} --> 变量:类型断言
type Student struct {
Name string
Age int
}
func ReflectTest01(b interface{}) {
// 通过反射获取传入的变量的type,kind值
rType := reflect.TypeOf(b)
fmt.Println("rTpye = ", rType)
// 获取到reflect.Value
rVal := reflect.ValueOf(b)
fmt.Printf("rVal = %v, rval type = %T\n", rVal, rVal)
n2 := 2 + rVal.Int()
fmt.Println("n2 = ", n2)
// rVal 转成 interface{}
iV := rVal.Interface()
// iV 转成 变量
num := iV.(int)
fmt.Println("num = ", num)
}
func ReflectTest02(b interface{}) {
// 通过反射获取传入的变量的type,kind值
rType := reflect.TypeOf(b)
fmt.Println("rTpye = ", rType)
// 获取到reflect.Value
rVal := reflect.ValueOf(b)
fmt.Printf("rVal = %v, rval type = %T\n", rVal, rVal)
// rVal 转成 interface{}
iV := rVal.Interface()
fmt.Printf("iv = %v, iv type = %T\n", iV, iV)
// iV 转成 变量
stu, ok := iV.(Student)
if ok {
fmt.Println(stu)
}
}
反射的注意事项和细节
- reflect.Value.Kind, 获取变量的类别,返回的是一个常量
- Type和Kind的区别
- Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的
- 通过反射可以在让变量在interface{} 和 Reflect.Value之间相互转换:变量 <---> interface{} <---> reflect.Value
- 使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(), 而不能使用其他的,否则包panic
- 通过反射的来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,它融合是需要使用到reflect.Value.Elem()方法
写出反射的几个例子
- 使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
- 使用反射的方式来获取结构体的tag标签,遍历字段的值,修改字段值,调用结构体方法
- 定义了两个函数test1和test2,定义一个适配器函数用统一处理接口
- 使用反射操作任意结构体类型
- 使用反射创建并操作结构体