zoukankan      html  css  js  c++  java
  • Golang反射

    反射

    • 基本介绍
    1. 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
    2. 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
    3. 通过反射,可以修改变量的值,可以调用关联的方法。
    4. 使用反射,需要 import (“reflect”)
    • 应用场景
    1. 不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。
    2. 对结构体序列化时,如果结构体有指定Tag,也会使用到反射生成对应的字符串。
    • 常用函数和概念
    1. reflect.TypeOf(变量名), 获取变量的类型,返回reflect.Type类型
        //使用 reflect.TypeOf() 函数可以获得任意值的类型对象,通过类型对象可以访问任意值的类型信息。
    	rTyp := reflect.TypeOf(b) 
    	fmt.Println("rType=", rTyp)
    
    1. reflect.ValueOf(变量名), 获取变量的值,返回reflect.

      Value类型reflect.Value是-一个结构体类型。通过reflect.Value,可以获取到关于该变量的很多信息。

        //使用 reflect.TypeOf() 函数可以获得任意值的类型对象,通过类型对象可以访问任意值的类型信息。
    	rTyp := reflect.TypeOf(b) 
    	fmt.Println("rType=", rTyp)
    
    1. 变量interface{} 和 reflect.Value 是可以相互转换的.
        var num int = 100
        rVal := reflect.ValueOf(b)
        //下面我们将 rVal 转成 interface{}
    	iV := rVal.Interface()
    	//将 interface{} 通过断言转成需要的类型
    	num2 := iV.(int)
    	fmt.Println("num2=", num2)
    
    1. reflect.TypeOf.Elem() 获取指针类型的元素类型
    type Student struct {
    	Name string `json:"username"`
    	Age int
    }
    func main() {
        stu := Student{
    		Name : "tom",
    		Age : 20,
    	}
        rTyp := reflect.TypeOf(stu)
    
        // 获取指针类型的元素类型
        e := rTyp.Elem()
        // 显示指针变量指向元素的类型名称和种类
        fmt.Printf("name:'%v' kind:'%v'
    ", e.Name(), e.Kind()) // name:'User' kind:'struct'
    }
    
    1. 任意值通过 reflect.TypeOf() 获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的 NumField() 和 Field() 方法获得结构体成员的详细信息。
        type User struct {
            Name   string `json:"username"`
            Age    int
            Salary float64
        }
    
        func main() {
            user := User{"pd", 18, 9999.99}
            tf := reflect.TypeOf(user)
            // 遍历结构体所有成员
            for i := 0; i < tf.NumField(); i++ {
                // 获取每个成员的结构体字段类型
                fieldType := tf.Field(i)
                fmt.Printf("name:'%v' tag:'%v'
    ", fieldType.Name, fieldType.Tag)
                // name:'Name' tag:'json:"username"'
                // name:'Age' tag:''
                // name:'Salary' tag:''
            }
            // 通过字段名, 找到字段类型信息
            userType, ok := tf.FieldByName("Name")
            if ok {
                // 从tag中取出需要的tag
                fmt.Println(userType.Tag.Get("json")) // username
            }
        }
    
    1. 通过反射获取值信息
        func main() {
            // 声明整型变量a并赋初值
            var a int
            a = 666
            // 获取变量a的反射值对象
            vf := reflect.ValueOf(a)
            // 将vf反射值对象以Interface{}类型取出, 通过类型断言转换为int类型
            r1 := vf.Interface().(int)
            // 将vf反射值对象以int64类型取出
            r2 := vf.Int()
            // 强制类型转换为int类型
            r3 := int(r2)
            fmt.Printf("r1值:%v r1类型:%T
    ", r1, r1) // r1值:666 r1类型:int
            fmt.Printf("r2值:%v r2类型:%T
    ", r2, r2) // r2值:666 r2类型:int64
            fmt.Printf("r3值:%v r3类型:%T
    ", r3, r3) // r3值:666 r3类型:int
        }
    
    1. 通过反射访问结构体成员的值
        type User struct {
            Name   string
            Age    int
            Salary float64
        }
    
        func main() {
            user := User{"pd", 18, 9999.99}
            vf := reflect.ValueOf(user)
            // 获取字段数量
            fmt.Printf("NumField:%v
    ", vf.NumField()) // NumField:3
            // 获取索引为2的字段
            field := vf.Field(2)
            fmt.Println(field.Type()) // float64
            // 根据名字查找字段
            fbn := vf.FieldByName("Name")
            fmt.Println(fbn.Type()) // string
            // 根据索引查找字段
            fbi := vf.FieldByIndex([]int{1})
            fmt.Println(fbi.Type()) // int
        }
    
    1. 判断反射值的空和有效性
        func main() {
            // *int的空指针
            var a *int
            fmt.Println(reflect.ValueOf(a).IsNil()) // true
    
            // nil值
            fmt.Println(reflect.ValueOf(nil).IsValid()) // false
    
            // 实例化一个结构体
            s := struct{}{}
            // 尝试从结构体中查找一个不存在的字段
            fmt.Println(reflect.ValueOf(s).FieldByName("").IsValid()) // false
    
            // 尝试从结构体中查找一个不存在的方法
            fmt.Println(reflect.ValueOf(s).MethodByName("").IsValid()) // false
        }
    
    1. 通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方法
        var num int = 10
        //2. 获取到 reflect.Value
    	rVal := reflect.ValueOf(num)
    	//3. Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
    	rVal.Elem().SetInt(20)
    
        fmt.Println("num=", num) // 20
    
    1. 通过类型信息创建实例
        func main() {
            var a int
            // 取变量a的反射类型对象
            tf := reflect.TypeOf(a)
            // 根据反射类型对象创建这个类型的实例值,值以 reflect.Value 类型返回
            obj := reflect.New(tf)
            // 输出类型和种类
            fmt.Printf("type:%v kind:%v
    ", obj.Type(), obj.Kind()) // type:*int kind:ptr
        }
    
    1. 通过反射调用函数、方法
    // add函数
    func add(a, b int) int {
        return a + b
    }
    
    func main() {
        // 将函数包装为反射值对象
        vf := reflect.ValueOf(add)
        // 构造函数参数, 传入两个整型值
        paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
        // 反射调用函数
        retList := vf.Call(paramList)
        // 获取第一个返回值, 取整数值
        fmt.Println(retList[0].Int()) // 30
    }
    
    • 注意事项
    1. reflect.Value.Kind,获取变量的类别,返回的是一个常量
    2. Type 和 Kind 的区别

      Type 是类型, Kind 是类别, Type 和 Kind 可能是相同的,也可能是不同的.

        rTyp := reflect.TypeOf(b)
    	rVal := reflect.ValueOf(b)
        //(1) rVal.Kind() ==> 
    	kind1 := rVal.Kind()
    	//(2) rTyp.Kind() ==>
    	kind2 := rTyp.Kind()
    
    1. 通过反射可以在让变量在interface{} 和Reflect.Value之间相互转换.
    2. 使用反射的方式来获取交量的值(并返回对应的类型),要求数据类型匹配
        var num int = 100
    	rTyp := reflect.TypeOf(num)
    	rVal := reflect.ValueOf(num)
    	
    	n2 := 2 + rVal.Int()
    	//n3 := rVal.Float() //error panic
    
    1. 通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方法
        var num int = 10
        //2. 获取到 reflect.Value
    	rVal := reflect.ValueOf(num)
    	//3. Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
    	rVal.Elem().SetInt(20)
    
        fmt.Println("num=", num) // 20
    
    1. reflect.Value.Elem() 用于获取指针指向变量
        type User struct {
            Name   string
            Age    int
            Salary float64
        }
        func main() {
            // 声明一个空结构体
            type User struct {}
            // 创建User的实例
            user := &User{}
            // 获取结构体实例的反射类型对象
            t := reflect.TypeOf(user)
            // 显示反射类型对象的名称和种类
            fmt.Printf("name:'%v' kind:'%v'
    ", t.Name(), t.Kind()) // name:'' kind:'ptr'
            // 获取指针类型的元素类型
            e := t.Elem()
            // 显示指针变量指向元素的类型名称和种类
            fmt.Printf("name:'%v' kind:'%v'
    ", e.Name(), e.Kind()) // name:'User' kind:'struct'
        }
    
  • 相关阅读:
    SpringBoot+mybatis的驼峰命名转换不生效
    vue3 ts遇到的问题
    阿里巴巴的Java 工程脚手架
    Mybatis获取插入值的ID
    Bootstrap的Modal与WebUploader联用的问题及办法
    Flex布局专题
    23种设计模式
    排序算法-Java实现快速排序算法
    中间件面试专题:kafka高频面试问题
    中间件面试专题:RabbitMQ高频面试问题
  • 原文地址:https://www.cnblogs.com/KylinBlog/p/13607277.html
Copyright © 2011-2022 走看看