反射
- 基本介绍
- 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
- 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法。
- 使用反射,需要 import (“reflect”)
- 应用场景
- 不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。
- 对结构体序列化时,如果结构体有指定Tag,也会使用到反射生成对应的字符串。
- 常用函数和概念
- reflect.TypeOf(变量名), 获取变量的类型,返回reflect.Type类型
//使用 reflect.TypeOf() 函数可以获得任意值的类型对象,通过类型对象可以访问任意值的类型信息。
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
- reflect.ValueOf(变量名), 获取变量的值,返回reflect.
Value类型reflect.Value是-一个结构体类型。通过reflect.Value,可以获取到关于该变量的很多信息。
//使用 reflect.TypeOf() 函数可以获得任意值的类型对象,通过类型对象可以访问任意值的类型信息。
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
- 变量interface{} 和 reflect.Value 是可以相互转换的.
var num int = 100
rVal := reflect.ValueOf(b)
//下面我们将 rVal 转成 interface{}
iV := rVal.Interface()
//将 interface{} 通过断言转成需要的类型
num2 := iV.(int)
fmt.Println("num2=", num2)
- reflect.TypeOf.Elem() 获取指针类型的元素类型
type Student struct {
Name string `json:"username"`
Age int
}
func main() {
stu := Student{
Name : "tom",
Age : 20,
}
rTyp := reflect.TypeOf(stu)
// 获取指针类型的元素类型
e := rTyp.Elem()
// 显示指针变量指向元素的类型名称和种类
fmt.Printf("name:'%v' kind:'%v'
", e.Name(), e.Kind()) // name:'User' kind:'struct'
}
- 任意值通过 reflect.TypeOf() 获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的 NumField() 和 Field() 方法获得结构体成员的详细信息。
type User struct {
Name string `json:"username"`
Age int
Salary float64
}
func main() {
user := User{"pd", 18, 9999.99}
tf := reflect.TypeOf(user)
// 遍历结构体所有成员
for i := 0; i < tf.NumField(); i++ {
// 获取每个成员的结构体字段类型
fieldType := tf.Field(i)
fmt.Printf("name:'%v' tag:'%v'
", fieldType.Name, fieldType.Tag)
// name:'Name' tag:'json:"username"'
// name:'Age' tag:''
// name:'Salary' tag:''
}
// 通过字段名, 找到字段类型信息
userType, ok := tf.FieldByName("Name")
if ok {
// 从tag中取出需要的tag
fmt.Println(userType.Tag.Get("json")) // username
}
}
- 通过反射获取值信息
func main() {
// 声明整型变量a并赋初值
var a int
a = 666
// 获取变量a的反射值对象
vf := reflect.ValueOf(a)
// 将vf反射值对象以Interface{}类型取出, 通过类型断言转换为int类型
r1 := vf.Interface().(int)
// 将vf反射值对象以int64类型取出
r2 := vf.Int()
// 强制类型转换为int类型
r3 := int(r2)
fmt.Printf("r1值:%v r1类型:%T
", r1, r1) // r1值:666 r1类型:int
fmt.Printf("r2值:%v r2类型:%T
", r2, r2) // r2值:666 r2类型:int64
fmt.Printf("r3值:%v r3类型:%T
", r3, r3) // r3值:666 r3类型:int
}
- 通过反射访问结构体成员的值
type User struct {
Name string
Age int
Salary float64
}
func main() {
user := User{"pd", 18, 9999.99}
vf := reflect.ValueOf(user)
// 获取字段数量
fmt.Printf("NumField:%v
", vf.NumField()) // NumField:3
// 获取索引为2的字段
field := vf.Field(2)
fmt.Println(field.Type()) // float64
// 根据名字查找字段
fbn := vf.FieldByName("Name")
fmt.Println(fbn.Type()) // string
// 根据索引查找字段
fbi := vf.FieldByIndex([]int{1})
fmt.Println(fbi.Type()) // int
}
- 判断反射值的空和有效性
func main() {
// *int的空指针
var a *int
fmt.Println(reflect.ValueOf(a).IsNil()) // true
// nil值
fmt.Println(reflect.ValueOf(nil).IsValid()) // false
// 实例化一个结构体
s := struct{}{}
// 尝试从结构体中查找一个不存在的字段
fmt.Println(reflect.ValueOf(s).FieldByName("").IsValid()) // false
// 尝试从结构体中查找一个不存在的方法
fmt.Println(reflect.ValueOf(s).MethodByName("").IsValid()) // false
}
- 通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方法
var num int = 10
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(num)
//3. Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
rVal.Elem().SetInt(20)
fmt.Println("num=", num) // 20
- 通过类型信息创建实例
func main() {
var a int
// 取变量a的反射类型对象
tf := reflect.TypeOf(a)
// 根据反射类型对象创建这个类型的实例值,值以 reflect.Value 类型返回
obj := reflect.New(tf)
// 输出类型和种类
fmt.Printf("type:%v kind:%v
", obj.Type(), obj.Kind()) // type:*int kind:ptr
}
- 通过反射调用函数、方法
// add函数
func add(a, b int) int {
return a + b
}
func main() {
// 将函数包装为反射值对象
vf := reflect.ValueOf(add)
// 构造函数参数, 传入两个整型值
paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
// 反射调用函数
retList := vf.Call(paramList)
// 获取第一个返回值, 取整数值
fmt.Println(retList[0].Int()) // 30
}
- 注意事项
- reflect.Value.Kind,获取变量的类别,返回的是一个常量
- Type 和 Kind 的区别
Type 是类型, Kind 是类别, Type 和 Kind 可能是相同的,也可能是不同的.
rTyp := reflect.TypeOf(b)
rVal := reflect.ValueOf(b)
//(1) rVal.Kind() ==>
kind1 := rVal.Kind()
//(2) rTyp.Kind() ==>
kind2 := rTyp.Kind()
- 通过反射可以在让变量在interface{} 和Reflect.Value之间相互转换.
- 使用反射的方式来获取交量的值(并返回对应的类型),要求数据类型匹配
var num int = 100
rTyp := reflect.TypeOf(num)
rVal := reflect.ValueOf(num)
n2 := 2 + rVal.Int()
//n3 := rVal.Float() //error panic
- 通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方法
var num int = 10
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(num)
//3. Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
rVal.Elem().SetInt(20)
fmt.Println("num=", num) // 20
- reflect.Value.Elem() 用于获取指针指向变量
type User struct {
Name string
Age int
Salary float64
}
func main() {
// 声明一个空结构体
type User struct {}
// 创建User的实例
user := &User{}
// 获取结构体实例的反射类型对象
t := reflect.TypeOf(user)
// 显示反射类型对象的名称和种类
fmt.Printf("name:'%v' kind:'%v'
", t.Name(), t.Kind()) // name:'' kind:'ptr'
// 获取指针类型的元素类型
e := t.Elem()
// 显示指针变量指向元素的类型名称和种类
fmt.Printf("name:'%v' kind:'%v'
", e.Name(), e.Kind()) // name:'User' kind:'struct'
}