Golang 反射

Golang 反射

基本介绍

注意通过指针变量获取的变量是地址

	//指针变量的kind
	fmt.Printf("%v\n", reflect.TypeOf(&student).Kind()) //ptr
	//指针变量的 type
	fmt.Printf("%T\n", &student)      //*main.Student
	//区别于student.Name == (*student).Name
	fmt.Printf("%v\n", &student.Name) //0xc0000044a0
	//指针变量的type
	fmt.Printf("%T\n", &student.Name) //*string
  • 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type), 类别(kind),type和kind可能一样

    type -> int,float,main.Student, *main.Student

    kind - > int, float, struct, ptr

    func main() {
    	var name string = "咖啡色的羊驼"
    	fmt.Printf("%v\n", reflect.TypeOf(name)) //string
    	fmt.Printf("%v\n", reflect.TypeOf(name).Kind())//string
    	s := stud.Student{
    		Name: "张三",
    		Age:  12,
    	}
    	fmt.Printf("%v\n", reflect.TypeOf(s))//stud.Student
    	fmt.Printf("%v\n", reflect.TypeOf(s).Kind())//struct
    }
    
  • 如果是结构体变量, 还可以获取到结构体本身的信息(包括结构体的字段, 方法)

    type Student struct {
    	Name string
    	Age int
    }
    func main() {
    	student := Student{
    		Name: "张三",
    		Age:  12,
    	}
    	val := reflect.ValueOf(student)
    	fmt.Printf("%T\n", val)
    	//相当于Java的getField()
    	name := val.FieldByName("Name")
    	//相当于Java的get, Java中String为引用类型
    	fmt.Println(name.String())
    	age := val.FieldByName("Age")
    	fmt.Println(age.Int())
    	
    	//相当于Java的newInstance()
    	instance := val.Interface()
    	fmt.Println(instance)
    	//类似Java返回的对象是Object, 需要强转
    	stu:= instance.(Student)
    	fmt.Println(stu.Name)
    	fmt.Println(stu.Age)
    }
    
  • 方法和字段需要对外公开才能获取到

基本操作

  • 获取类别

    func main() {
    	student := Student{
    		Name: "张三",
    		Age:  12,
    	}
    	rVal := reflect.ValueOf(student)
    	rTyp := reflect.TypeOf(student)
    	//相当于Java中getTypeName()
    	fmt.Println(rVal.Kind())
    	fmt.Println(rTyp.Kind())
    }
    
  • 获取字段

    func main() {
    	stu := Student{
    		Name:   "张三",
    		Age:    12,
    		Gender: false,
    	}
    	rVal := reflect.ValueOf(stu)
    	kind := rVal.Kind()
    	typ := rVal.Type()
    	if  kind!= reflect.Struct {
    		fmt.Println("error")
    		return
    	}
    	//如果操作对象不是结构体就会报错
    	for i := 0; i < rVal.NumField(); i++ {
    		val := rVal.Field(i)
    		//只有type才有Tag
    		tag := typ.Field(i).Tag.Get("json")
            //*(*string)(v.ptr)将ptr转为*string,然后取值
    		fmt.Println(val,tag)
    	}
    }
    
  • 如果修改值, 传入的必需是指针类型, 因为传入基本类型,通过值传递,函数内的对象地址不同

    func reflect02(inter interface{}) {
    	rVal := reflect.ValueOf(inter)
    	fmt.Printf("%T\n", rVal)
        //由于是值传递,这里对象的地址与外部的不同
    	fmt.Printf("%v\n", rVal)//0xc00003a1f0
    	fmt.Printf("%v\n", rVal.Kind())
    	//Elem()获取指针指向的对象
    	//rVal是地址.Elem()相当于取地址符*
    	rVal.Elem().SetInt(100)
    }
    func main() {
    	var num int = 12
    	fmt.Println(num)
    	//0xc00000a0f8
    	fmt.Println(&num)
    	reflect02(&num)
    	fmt.Println(num)
    }
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    func reflect01(inter interface{}) {
    	rVal := reflect.ValueOf(inter)
    	fmt.Println(rVal.Kind())
    	//Elem方法能返回指针变量指向的值
    	age := rVal.Elem().FieldByName("Age")
    	age.SetInt(19)
    }
    
    func main() {
    	student := Student{
    		Name: "张三",
    		Age:  12,
    	}
    	fmt.Println(student)
    	//如果想调用结构体中的指针变量, 无需使用取地址符
    	reflect01(&student)
    	fmt.Println(student)
    }
    
  • invoke方法

    type Student struct {
    	Name   string `json:"name"`
    	Age    int    `json:"age"`
    	Gender bool   `json:"gender"`
    }
    
    func (s Student) Add(a, b int) int {
    	fmt.Println("invoke add")
    	return a + b
    }
    
    func (s Student) Blank() {
    	fmt.Println("blank method")
    }
    func (this *Student) SetName(name string) {
    	this.Name = name
    }
    func main() {
    	stu := Student{
    		Name:   "张三",
    		Age:    12,
    		Gender: false,
    	}
    	rVal := reflect.ValueOf(stu)
    	kind := rVal.Kind()
    	if  kind!= reflect.Struct {
    		fmt.Println("error")
    		return
    	}
    	//rVal.Method()如果通过这种方式获取到的方法,方法按照字典序排序
    	//同样的方法也需要公开
    	blank := rVal.MethodByName("Blank")
    	blank.Call(nil)
    	add := rVal.MethodByName("Add")
    
    	//函数返回一个切片
    	res := add.Call(append([]reflect.Value{}, reflect.ValueOf(1), reflect.ValueOf(2)))
    	fmt.Println(res[0])
    
    	set := rVal.MethodByName("SetName")
    	set.Call(append([]reflect.Value{reflect.ValueOf("李四")}))
    	fmt.Println(stu)
    }
    

例子

func main() {
	stu := Student{
		Name:   "张三",
		Age:    12,
		Gender: false,
	}
	clazz := reflect.ValueOf(&stu)
	typ := reflect.TypeOf(&stu)
	if clazz.Kind() != reflect.Ptr {
		fmt.Println("不是指针,不能修改值")
		return
	}
	//这里要通过Elem()获取到地址指向的对象,才能操作
	num := clazz.Elem().NumField()
	for i := 0; i < num; i++ {
		tag := typ.Elem().Field(i).Tag.Get("json")
		if tag == "name"  {
			clazz.Elem().Field(i).SetString("李四")
		}
	}
	fmt.Println(stu)
}
posted @ 2020-08-15 23:49  CyberPelican  阅读(115)  评论(0编辑  收藏  举报