• Golang 反射


    Golang 反射

    基本介绍

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

    	//指针变量的kind
    	fmt.Printf("%v
    ", reflect.TypeOf(&student).Kind()) //ptr
    	//指针变量的 type
    	fmt.Printf("%T
    ", &student)      //*main.Student
    	//区别于student.Name == (*student).Name
    	fmt.Printf("%v
    ", &student.Name) //0xc0000044a0
    	//指针变量的type
    	fmt.Printf("%T
    ", &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
      ", reflect.TypeOf(name)) //string
      	fmt.Printf("%v
      ", reflect.TypeOf(name).Kind())//string
      	s := stud.Student{
      		Name: "张三",
      		Age:  12,
      	}
      	fmt.Printf("%v
      ", reflect.TypeOf(s))//stud.Student
      	fmt.Printf("%v
      ", 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
      ", 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
      ", rVal)
          //由于是值传递,这里对象的地址与外部的不同
      	fmt.Printf("%v
      ", rVal)//0xc00003a1f0
      	fmt.Printf("%v
      ", 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)
    }
    
  • 相关阅读:
    [转载] 美团-云鹏: 写给工程师的十条精进原则
    Docker测试一个静态网站
    Docker容器访问外部世界
    Docker容器间通信
    Docker网络(host、bridge、none)详细介绍
    Docker的资源限制(内存、CPU、IO)详细篇
    esxi中CentOS7不停机加磁盘并扩容现有分区
    ESXI6.5安装CentOS7教程
    Linux查看占用CPU和内存的 的程序
    Centos7使用脚本搭建LVS的DR模式。
  • 原文地址:https://www.cnblogs.com/kikochz/p/13511074.html
Copyright © 2020-2023  润新知