在编译时不知道类型的情况下,可更新变量、在运行时查看值、调用方法以及直接对它们的布局进行操作,这种机制被称为反射。
具体的应用场景大概如下:
动态地获取变量的各种信息(包括变量的类型type、类别kind);
如果是结构体变量,还可以获取结构体本身的字段、方法;
可以修改变量的值,调用变量的方法;
具体应用场景:
编写函数的适配器;
func funcName(funcPtr interface{},args ...interface{}){}在暂时未知调用哪个接口的时候,进行传参,传入的是可变参数args,这时候配合传入的函数指针funcPtr,利用反射,进行动态地调用函数。
func testInt(b interface{}) { //获取类型 rType := reflect.TypeOf(b) fmt.Println("rType:",rType) //获取值 rVal := reflect.ValueOf(b) n := rVal.Int() fmt.Printf("rVal value:%v , type: %T\n",rVal,rVal) fmt.Printf("n value: %v , type: %T \n",n,n) //获取interface{} Ir := rVal.Interface() fmt.Printf("Ir , value: %v , type: %T \n",Ir,Ir) //类型断言 num := Ir.(int) fmt.Printf("num , value: %v , type: %T \n",num,num) } func testStruct(b interface{}) { rType := reflect.TypeOf(b) fmt.Println("rType:",rType) //获取值 rVal := reflect.ValueOf(b) fmt.Printf("rVal value:%v , type: %T\n",rVal,rVal) //获取interface{} Ir := rVal.Interface() fmt.Printf("Ir , value: %v , type: %T \n",Ir,Ir) rKind := rVal.Kind() //表示数据类别 fmt.Printf("rkind , kind: %v , type: %T \n",rKind,rKind) //类型断言 num ,ok:= Ir.(Student) if ok { fmt.Printf("num , value: %v , type: %T \n", num, num) fmt.Println(num.Name) } }
对结构体进行序列化,需要制定Tag。
在对函数结构体序列化的时候,自定义Tag用到了反射,生成相对应的字符串。
reflect 包及相关常用函数 type type Kind uintKind代表Type类型值表示的具体分类。零值表示非法分类。
type type Type interface { ... }Type类型用来表示一个go类型。
不是所有go类型的Type值都能使用所有方法。请参见每个方法的文档获取使用限制。在调用有分类限定的方法时,应先使用Kind方法获知类型的分类。调用该分类不支持的方法会导致运行时的panic。
func func TypeOf(i interface{}) TypeTypeOf返回接口中保存的值的类型,TypeOf(nil)会返回nil。
type type Value struct { // 内含隐藏或非导出字段 }Value为go值提供了反射接口。
不是所有go类型值的Value表示都能使用所有方法。请参见每个方法的文档获取使用限制。在调用有分类限定的方法时,应先使用Kind方法获知该值的分类。调用该分类不支持的方法会导致运行时的panic。
Value类型的零值表示不持有某个值。零值的IsValid方法返回false,其Kind方法返回Invalid,而String方法返回"",所有其它方法都会panic。绝大多数函数和方法都永远不返回Value零值。如果某个函数/方法返回了非法的Value,它的文档必须显式的说明具体情况。
如果某个go类型值可以安全的用于多线程并发操作,它的Value表示也可以安全的用于并发。
func func ValueOf(i interface{}) ValueValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。
func (Value) func (v Value) Kind() KindKind返回v持有的值的分类,如果v是Value零值,返回值为Invalid
func (Value) func (v Value) Elem() ValueElem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
unc (Value) func (v Value) NumField() int返回v持有的结构体类型值的字段数,如果v的Kind不是Struct会panic
func (Value) func (v Value) Field(i int) Value返回结构体的第i个字段(的Value封装)。如果v的Kind不是Struct或i出界会panic
func (Value) func (v Value) NumMethod() int返回v持有值的方法集的方法数目。
func (Value) func (v Value) Method(i int) Value返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果i出界,或者v的持有值是接口类型的零值(nil),会panic。
func (Value) func (v Value) MethodByName(name string) Value返回v的名为name的方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果未找到该方法,会返回一个Value零值。
更多其它类型以及函数:Go语言标准库文档。
注意事项及细节
变量、interface{} 和 reflect.Value 是可以相互转换的。
reflect.Value.Kind,获取变量的类别,返回的是一个常量
Type 和 Kind 的区别
Type 是类型, Kind是类别, Type 和Kind 可能是相同的,也可能是不同的。
比如: var num int = 10, num 的 Type 是 int , Kind 也是 int;
比如: var stu Student stu 的Type 是 packageXXX.Student , Kind 是 struct 。
通过反射的来修改变量, 注意当使用SetXxx 方法来设置,需要通过传入对应的指针类型来完成, 这样才能改变传入的变量的值;