简介
Golang提供了一种机制,在编译时不知道类型的情况下,可更新变量、运行时查看值、调用方法以及直接对他们的布局进行操作的机制,称为反射。
为什么使用反射?
打个比方,有时候我们需要一个函数可以处理各种类型的值。在不知道类型的情况下,你可能会这么写:
switch value.(type) {case string:// ...一些操作case int:// ...一些操作case cbsStruct: // 自定义的结构体// ...一些操作// ...}
这边存在一个问题:类型很多,这个函数会写的非常长,而且还可能存在自定的类型,也就是说这个判断日后可能还要一直改,因为无法知道未知值到底属于什么类型。
无法透视一个未知类型的时候,以上代码其实不是很合理,这时候就需要有反射来帮忙你处理,反射使用TypeOf和ValueOf函数从接口中获取目标对象的信息,轻松完成目的。
Type和Value
| 类型 | 作用 |
|---|---|
| reflect.ValueOf() | 获取输入参数接口中的数据的值,如果为空则返回0 <- 注意是0 |
| reflect.TypeOf() | 动态获取输入参数接口中的值的类型,如果为空则返回nil <- 注意是nil |
类型之间的转换
int例子
func reflect1(num interface{}){rtype :=reflect.TypeOf(num)//获取到reflect.Typefmt.Printf("%v-%T\n",rtype,rtype)//int-*reflect.rtype,注意这不是基本数据类型中的intrval :=reflect.ValueOf(num)//获取到reflect.Valuefmt.Printf("%v-%T\n",rval,rval)//100-reflect.Value//rval+=2 编译不通过,rval的类型是reflect.Value,并不是intn:=rval.Int()+2fmt.Println(n)//102//将reflect.Value转为空接口iv :=rval.Interface()fmt.Printf("%v-%T\n",iv,iv)//100-int,空接口陷阱,运行时知道iv是int类型,但编译时不知//将接口通过断言,转为自己需要的类型num2:=iv.(int)fmt.Println(fmt.Printf("%v-%T\n",num2,num2))//100-int}func main() {var num int =100reflect1(num)}
struct例子
type Student struct {Name stringAge int}func reflect2(stu interface{}){rval :=reflect.ValueOf(stu)iv :=rval.Interface()//{jack 20}-main.Studentjack//iv.Name,编译出错,空接口陷阱fmt.Printf("%v-%T\n",iv,iv)student:=iv.(Student)fmt.Println(student.Name)//jack}func main() {stu :=Student{"jack",20}reflect2(stu)}
通过反射改变的值
int例子
func demo2test1(num interface{}) {rv :=reflect.ValueOf(num)fmt.Println(rv.Kind())rv.Elem().SetInt(100)}func main() {var num = 1demo2test1(&num)//这里要传地址fmt.Println(num)}
struct例子
type Person struct {Name stringAge int}//通过反射,改变struct的值func demo2test2(p interface{}) {rv :=reflect.ValueOf(p)fmt.Println(rv.Elem().Kind())rv.Elem().FieldByName("Name").SetString("李四")}func main() {p:=Person{"张三",20}demo2test2(&p)fmt.Println(p)}
通过反射调用方法
type Human struct {Name string `tag:"name"`Age intSex string}func(this Human) String(){fmt.Println(this.Name,this.Age,this.Sex)}func(this Human) Getnum(i int,n int) int{return i+n}func test(human interface{}){rtype :=reflect.TypeOf(human)rval :=reflect.ValueOf(human)kd :=rval.Kind()if kd !=reflect.Struct{panic("the value must be struct")}//拿到该结构有多少字段并遍历filednum :=rval.NumField()for i:=0;i<filednum;i++{fmt.Printf("第%v个字段的值为:%v\n",i,rval.Field(i))//获取标签,并显示tag :=rtype.Field(i).Tag.Get("tag")if tag!=""{fmt.Printf("第%v个字段的标签为:%v\n",i,tag)}}//拿到该结构有多少方法methodnum:=rval.NumMethod()fmt.Printf("该结构体有%v个方法\n",methodnum)//调用方法//注意这里的方法下标不是方法的顺序,是方法名按ASCII码排序的下标,这里调的是Stringrval.Method(1).Call(nil)//rval.MethodByName("string").Call(nil)//调用Getnumvar params []reflect.Valueparams=append(params,reflect.ValueOf(1))params=append(params,reflect.ValueOf(2))res :=rval.Method(0).Call(params)//参数与返回值都要是[]reflect.Valuefmt.Println(res[0].Int())}func main() {h:=Human{"张三",20,"男"}test(h)}
