import “reflect”
reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,
1、通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。
2、调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。
3、Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。
接下来通过几个代码来演示
package main
import (
"reflect"
"fmt"
)
//专门演示反射
func reflectTest01(b interface{}) {
//通过反射获取的传入的变量的 type , kind, 值
//1. 先获取到 reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(b)
n2 := 2 + rVal.Int()
//n3 := rVal.Float()
fmt.Println("n2=", n2)
//fmt.Println("n3=", n3)
fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)
//下面我们将 rVal 转成 interface{}
iV := rVal.Interface()
//将 interface{} 通过断言转成需要的类型
num2 := iV.(int)
fmt.Println("num2=", num2)
}
//专门演示反射[对结构体的反射]
func reflectTest02(b interface{}) {
//通过反射获取的传入的变量的 type , kind, 值
//1. 先获取到 reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rType=", rTyp)
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(b)
//3. 获取 变量对应的Kind
//(1) rVal.Kind() ==>
kind1 := rVal.Kind()
//(2) rTyp.Kind() ==>
kind2 := rTyp.Kind()
fmt.Printf("kind =%v kind=%v\n", kind1, kind2)
//下面我们将 rVal 转成 interface{}
iV := rVal.Interface()
fmt.Printf("iv=%v iv type=%T \n", iV, iV)
//将 interface{} 通过断言转成需要的类型
//这里,我们就简单使用了一带检测的类型断言.
//同学们可以使用 swtich 的断言形式来做的更加的灵活
stu, ok := iV.(Student)
if ok {
fmt.Printf("stu.Name=%v\n", stu.Name)
}
}
type Student struct {
Name string
Age int
}
type Monster struct {
Name string
Age int
}
func main() {
//请编写一个案例,
//演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作
//1. 先定义一个int
var num int = 100
reflectTest01(num)
//2. 定义一个Student的实例
stu := Student{
Name : "tom",
Age : 20,
}
reflectTest02(stu)
}
输出
rType= int
n2= 102
rVal=100 rVal type=reflect.Value
num2= 100
///func2
rType= main.Student
kind =struct kind=struct
iv={tom 20} iv type=main.Student
stu.Name=tom
该代码用普通类型 int和结构体 struct来实验。
函数的参数要用interface{}), 例如func reflectTest01(b interface{}) {…}
都用到了
reflect.TypeOf(b) 、 reflect.ValueOf(b) 和 rVal.Interface()
这几个方法,所以说这几个方法很普遍。
它们是可以相互转换的
例子2
package main
import (
"reflect"
"fmt"
)
//通过反射,修改,
// num int 的值
// 修改 student的值
func reflect01(b interface{}) {
//2. 获取到 reflect.Value
rVal := reflect.ValueOf(b)
// 看看 rVal的Kind是
fmt.Printf("rVal kind=%v\n", rVal.Kind())
//3. rVal
//Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
rVal.Elem().SetInt(20)
}
func main() {
var num int = 10
reflect01(&num)
fmt.Println("num=", num) // 20
//你可以这样理解rVal.Elem()
// num := 9
// ptr *int = &num
// num2 := *ptr //=== 类似 rVal.Elem()
}
输出
rVal kind=ptr
num= 20
主函数传入地址,意味着要修改变量,所以 rVal的 kind是 ptr,但是如果直接用 rVal.SetInt(20) 是会报错的,因为SetInt是 value类型的方法,而不是 *value
这就要用到我们的 Elem()方法了,rVal.Elem(), 类似于取值符号 “ * ”
你可以这样理解 rVal.Elem()
num := 10
var ptr *int = &num
num2 := *ptr //=== 类似 rVal.Elem()
下一个例子是反射的最佳应用(尚硅谷教程)
package main
import (
"fmt"
"reflect"
)
//定义了一个Monster结构体
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32 `json:"成绩"`
Sex string
}
//方法,返回两个数的和
func (s Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
//方法, 接收四个值,给s赋值
func (s Monster) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
//方法,显示s的值
func (s Monster) Print() {
fmt.Println("---start~----")
fmt.Println(s)
fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
//获取reflect.Type 类型
typ := reflect.TypeOf(a)
//获取reflect.Value 类型
val := reflect.ValueOf(a)
//获取到a对应的类别
kd := val.Kind()
//如果传入的不是struct,就退出
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}
//获取到该结构体有几个字段
num := val.NumField()
fmt.Printf("struct has %d fields\n", num) //4
//变量结构体的所有字段
for i := 0; i < num; i++ {
fmt.Printf("Field %d: 值为=%v\n", i, val.Field(i))
//获取到struct标签, 注意需要通过reflect.Type来获取tag标签的值
tagVal := typ.Field(i).Tag.Get("json")
//如果该字段于tag标签就显示,否则就不显示
if tagVal != "" {
fmt.Printf("Field %d: tag为=%v\n", i, tagVal)
}
}
//获取到该结构体有多少个方法
numOfMethod := val.NumMethod()
fmt.Printf("struct has %d methods\n", numOfMethod)
//var params []reflect.Value
//方法的排序默认是按照 函数名的排序(ASCII码)
val.Method(1).Call(nil) //获取到第二个方法。调用它
//调用结构体的第1个方法Method(0)
var params []reflect.Value //声明了 []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params) //传入的参数是 []reflect.Value, 返回[]reflect.Value
fmt.Println("res=", res[0].Int()) //返回结果, 返回的结果是 []reflect.Value*/
}
func main() {
//创建了一个Monster实例
var a Monster = Monster{
Name: "黄鼠狼精",
Age: 400,
Score: 30.8,
}
//将Monster实例传递给TestStruct函数
TestStruct(a)
}
struct has 4 fields
Field 0: 值为=黄鼠狼精
Field 0: tag为=name
Field 1: 值为=400
Field 1: tag为=monster_age
Field 2: 值为=30.8
Field 2: tag为=成绩
Field 3: 值为=
struct has 3 methods
---start~----
{黄鼠狼精 400 30.8 }
---end~----
res= 50
value.Field(i) 和 type.Field(i) 两者的返回值完换不同
1、
2、
而 type.field(i)返回类型是StructField,它又是一个结构体
两个都是对 struct里的变量进行查看
value.Field(i) 返回一个 value类型,只能看一看值,不能对值加减运算。
type.Field(i) 返回 StructField 类型,它又是一个 struct, 里面的 tag 变量尤其重要
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
tag 是什么呢,就是这个 “json:” , 用 get方法可以反射获得 name的值。
tagVal := typ.Field(i).Tag.Get("json")
这句代码就可以理解了。
第二个要注意的点是
//方法的排序默认是按照 函数名的排序(ASCII码)
val.Method(1).Call(nil) //获取到第二个方法。调用它
value的 Method 方法,通过它可以调用 value结构体的方法,它调用的顺序是函数名的ascii排序
GetSum、Set、Print这三个函数的顺序应该是GetSum、Print、Set
故调用第二个,Print
例4
package test
import (
"reflect"
"testing"
)
type user struct {
UserId string
Name string
}
func TestReflectStruct(t *testing.T) {
var (
model *user //这里是指针
st reflect.Type
elem reflect.Value
)
st = reflect.TypeOf(model)
t.Log("reflect.TypeOf", st.Kind().String()) //ptr
st = st.Elem()
t.Log("reflect.TypeOf.Elem", st.Kind().String()) //struct 取到了值
//新建一个指向 type的指针
elem = reflect.New(st)
t.Log("reflect.New", elem.Kind().String()) //ptr
t.Log("reflect.New.Elem", elem.Elem().Kind().String()) //struct
model = elem.Interface().(*user) //model 的值是一个指针,指针指向 usr空间
elem = elem.Elem() //elem 现在直接指向了user空间
elem.FieldByName("UserId").SetString("12345678")
elem.FieldByName("Name").SetString("xiaopang")
t.Log("model, model.Name", model, model.Name)
}
=== RUN TestReflectStruct
reflect_test.go:20: reflect.TypeOf ptr
reflect_test.go:22: reflect.TypeOf.Elem struct
reflect_test.go:25: reflect.New ptr
reflect_test.go:26: reflect.New.Elem struct
reflect_test.go:33: model, model.Name &{12345678 xiaopang} xiaopang
--- PASS: TestReflectStruct (0.00s)
PASS
通过反射,可以创建 struct,reflect.New(st)
还可以设置变量的值
elem.FieldByName(“UserId”).SetString(“12345678”)
找值-FieldByName, 设置值-SetString
st = st.Elem()
这个是 type的 elem方法,返回该类型的元素类型,如果该类型的Kind不是Array、Chan、Map、Ptr或Slice,会panic
也就是 *st,指针变成真实类型 struct
elem = reflect.New(st)
从输出可以看到,它和 st (也就是 ptr type)是一样的,它的类型是 reflect.Value,它指向的值是 reflect.Type
之后新建了一个 model变量,它是指向 user的指针,而 elem也是指向 user(疑问,是指向 type,但是它还是指向 user)。