reflection 反射
反射可大大提高程序的灵活性,使得interface{}有更大的发挥余地
反射使用TypeOf和ValueOf函数从接口中获取目标对象信息
反射会将匿名字段作为独立字段(匿名字段本质)
想要利用反射修改对象状态,前提是interface.data是settable,即pointer-interface
通过反射可以"动态"调用方法
举例
1、通过反射来获取属性信息,方法信息
//反射 练习//获取字段的类型信息,方法信息, 属性信息package mainimport ( "fmt" "reflect")type User struct { Id int Name string Age int}func (user User) Hello(){ fmt.Println("hello world")}func main() { user := User{Id:2, Name:"xiaoqiang",Age:30} info(user)}//定义一个方法//此方法接收的是一个空接口//这样的话,其实是任何类型都可以接收了func info(o interface{}) { //获取o的类型 t := reflect.TypeOf(o) fmt.Println("Type:\t", t.Name()) //取出类型后,需要对类型进行校验, //查看是否是规定的类型 if k :=t.Kind(); k != reflect.Struct { //如果不是规定的Struct类型的话,就需要进行 //异常处理 fmt.Println("输入参数的类型不匹配!") return } //获取o的属性 v := reflect.ValueOf(o) fmt.Println("Fields:\n") //开始遍历,输出字段 for i := 0; i < t.NumField(); i++ { //获取属性下标 f := t.Field(i) val := v.Field(i).Interface() //输出属性的名称,类型,对应的值 fmt.Printf("%6s:%v = %v\n", f.Name, f.Type, val) } //开始获取 方法的基本信息 for i:=0; i<t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("%6s:%v\n", m.Name, m.Type) }}
2、反射是如何 获取 匿名字段呢?
//反射 是如何处理 匿名字段的?package mainimport ( "reflect" "fmt")type Stu struct { Id int Name string Age int}type Man struct { //这里你要注意一下,你创建的属性,是有顺序的,是有下标的 //如Stu 下标 就是0, title下标就是1 // Stu 就是匿名属性 Stu title string}func main() { //注意,对匿名字段进行初始化时的方式,其实本质上跟其他属性是一样的 m := Man{Stu:Stu{Id:2,Name:"Jack",Age:19}, title:"Manager"} t := reflect.TypeOf(m) //取匿名字段的方式 //FieldByIndex 方法,传入的是一个切片slice类型 //第1个0,表示,匿名字段在Man中的下标位置 //第2个0,表示,你要取匿名字段中哪个属性的下标 fmt.Printf("%#v\n", t.FieldByIndex([]int{0,0})) //取的是id fmt.Printf("%#v\n", t.FieldByIndex([]int{0,1})) //取的是Name fmt.Printf("%#v\n", t.FieldByIndex([]int{0,2})) //取的是Age}
3、通过反射对基本类型进行修改
//如果通过反射,对基本类型进行修改package mainimport ( "reflect" "fmt")func main() { //下面测试,对基本类型的修改 操作 x := 456 //传递的参数是 地址 v := reflect.ValueOf(&x) //Elem方法,是取出元素值来,然后通过setint方法,进行重新设置 v.Elem().SetInt(789) fmt.Println(x)}
4、通过反射 对复杂类型进行修改
//如果通过反射,对复杂类型进行修改package mainimport ( "reflect" "fmt")type Teac struct { Id int Name string Age int}func main() { teac := Teac{Id:5,Name:"Ant-man",Age:23} fmt.Println("teac:\t", teac) //传递的是 地址哦 Set(&teac) fmt.Println("teac:\t", teac)}func Set(o interface{}) { v := reflect.ValueOf(o) if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { fmt.Printf("xxx") return }else{ v = v.Elem() } // 通过FieldByName 这个方法,直接输入 名称,来获取 f := v.FieldByName("Name") //校验,是否取到Name属性的值 if !f.IsValid() { fmt.Printf("BAD") return } //然后,再校验,类型是否匹配 if f.Kind() == reflect.String { f.SetString("Iron Man") }}
5、通过反射对方法进行动态调用
//通过反射,进行方法的调用,相当于动态调用了package mainimport ( "fmt" "reflect")type Teacher struct { Id int Name string Age int}//通过receiver将Show方法,跟Teacher类型,进行绑定func (teacher Teacher)Show(name string) { fmt.Println("hello, ", name, ", my name is ", teacher.Name)}//注意======目前没有发现====如何通过====反射===来获取=====私有方法func (teacher Teacher)info(){ fmt.Println("=====")}func main() { teacher := Teacher{Id:34, Name:"Thor",Age:34} teacher.Show("Hawkeye") //下面通过反射,调用show方法 v := reflect.ValueOf(teacher) //获取show方法 m := v.MethodByName("Show") //校验一下,是否获取到show方法呢 if !m.IsValid() { fmt.Printf("=======没有获取到制定的方法====") return } //参数必须是切片类型 //reflect.Value{} 这里面,可以设定多个参数类型 //目前,我们这里只有一个string类型的参数 // args := []reflect.Value{reflect.ValueOf("Hulk")} m.Call(args)}