- func TypeOf(i interface{}) Type:获取reflect.Type 类型
TypeOf返回接口中保存的值的类型,TypeOf(nil)会返回nil。 - func ValueOf(i interface{}) Value:获取reflect.Value 类型
ValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。 - 获取变量对应的类别kind,有两种方式:
(1)Type接口中的方法 Kind() Kind:Kind返回该接口的具体分类
(2)func (v Value) Kind() Kind:Kind返回v持有的值的分类,如果v是Value零值,返回值为Invalid - func (v Value) String() string:返回字符串形式
返回v持有的值的字符串表示。因为go的String方法的惯例,Value的String方法比较特别。和其他获取v持有值的方法不同:v的Kind是String时,返回该字符串;v的Kind不是String时也不会panic而是返回格式为"<T value>"的字符串,其中T是v持有值的类型。 - func (v Value) Interface() (i interface{}):
本方法返回v当前持有的值(表示为/保管在interface{}类型),等价于:新定义了一个空接口类型,并将v赋给空接口类型 - func (v Value) Elem() Value:v必须是指针类型或空接口类型,取得v指向的值
Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
=================结构体字段field============ - func (v Value) NumField() int:获取该结构体有几个字段,这里的Value是reflect.Value类型
返回v持有的结构体类型值的字段数,如果v的Kind不是Struct会panic - func (v Value) Field(i int) Value:返回结构体的第i个字段的值
返回结构体的第i个字段值(的Value封装)。如果v的Kind不是Struct或i出界会panic -
Field(i int) StructField:
返回struct类型的第i个字段的类型,如非结构体或者i不在[0, NumField())内将会panic
常用:
rTyp.Field(i).Tag.Get("json") :返回序列化的字段名。
序列化json的名字是可以自定义的,但是反序列化写死了就是json。所以我们统一使用json了 -
func (v Value) FieldByName(name string) Value:获得该字段的属性值。与.SetString连用,可以设置该字段的属性值。
返回该类型名为name的字段(的Value封装)(会查找匿名字段及其子字段),如果v的Kind不是Struct会panic;如果未找到会返回Value零值。
=================结构体方法method============ -
func (v Value) NumMethod() int:获取该结构体有多少个方法
返回v持有值的方法集的方法数目。 -
func (v Value) Method(i int) Value:找到和reflect.Value类型绑定的第i个方法
返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。
方法的排序跟方法写的位置没有关系,而是按照ASCII码排序的。从表面看就是首字母顺序。 -
func (v Value) Call(in []Value) []Value:调用方法
Call方法使用输入的参数in调用v持有的函数。 -
func (v Value) Int() int64:
返回v持有的有符号整数(表示为int64),如果v的Kind不是Int、Int8、Int16、Int32、Int64会panic
一、反射
- 如果想要自己写一套框架,反射必用无疑。
- 如我们常见的:在web页面输入用户名密码后点击提交,就能跳转到对应页面。中间过程需要进入.do等类里面处理,xml的映射等都是反射在底层操作的。
- 包括创建一个结构体或者直接给结构体字段赋值,反射都可以完成。
- 通常是创建一个实例,然后调用。但是是个怎样的调用过程的呢?反射是一个有规范的。比如testing框架,必须以TestXxx打头,否则就调不起来了,中间可能是扫描目录下所有_test.go文件中的TestXxx方法。
- 反射里面有大量的函数,反射是在函数的应用上扩展的。
- reflect.Type和reflect.Value是核心。
其中,Type是一个接口,里面有各种各样的方法;用的最多的是Value,Value是一个结构体。- Kind范围大于Type。如果Kind比作电器,Type就是冰箱。Type是类型,Kind是类别。
对基本数据类型来说,二者是一样的;对于结构体等是不一样的。
如:结构体:Type是 “包名.Student(如: main.Student)” ,Kind是struct- pkg1.Student和pkg2.Student可以同时存在,因为Go以包名区分
1.1 一个问题引出反射的使用场景
1.2 基本介绍
- 反射可以在运行时动态的获取变量的各种信息,比如变量的类型(type),类别(kind)
- 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法
- 使用反射,需要 import "reflect"
- 示意图:
1.3 反射重要的函数和概念
- 3)reflect.Value.Kind 获取变量的类别,返回的是一个常量
- 4)获取Kind有两种方式,分别是通过reflect.Type和reflect.Value。参考方法详细介绍里的代码
- 5)Type和Kind的区别
Kind范围大于Type。如果Kind比作电器,Type就是冰箱
Type是类型,Kind是类别。Type和Kind可能是相同的,也可能是不同的。
比如:var num int =10 -- num的Type是int,Kind也是int
比如:var stu Student -- stu的Type是包名.Student(如: main.Student),Kind是struct - 6)变量、interface{}和reflect.Value是可以相互转化的。这点在实际开发中,会经常使用到。示意图:
代码: - 为什么要用到类型断言呢?因为反射是在运行时才知道其数据类型的,编译时没法知道。所以需要类型断言,来保证编译时不报错。断言的最佳实践见下:
- 7)使用反射的方式来获取变量的值,并返回对应的类型,数据类型必须匹配。如:num是int类型,那么就应该使用reflect.ValueOf(num).Int(),否则报错panic
- 8)通过反射来修改变量,注意要使用SetXxx方法通过传入对应数据类型的指针类型来设置。这样才能改变传入变量的值。同时需要reflect.ValueOf(&num).Elem()方法
- 9)reflect.ValueOf(&num).Elem()方法怎么理解?
- 10) 序列化的标签如何获取?rTyp.Field(i).Tag.Get("json") :返回序列化的字段名。
序列化的时候,json字段是可以自己定义的。但是反序列化写死了就叫json
1.4 使用场景
1.5 快速入门-演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作
1.6 快速入门-演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
1.7 应用实例一
1、使用反射来遍历结构体的字段的值,调用结构体的方法
2、使用反射的方式获取结构体tag标签,修改字段值(都是通过地址传递的方式完成)
1.8 应用实例二 -- 桥连接
1.9 应用实例三 -- 使用反射操作任意结构体类型
1.10 应用实例四 -- 在底层直接创建结构体并赋值