reflect包实现了运行时反射,允许程序操作任意类型的对象。 reflect配合interface{}使用,为go增加了动态的特性。
反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息
一、两个重要类型:Type & Value
调用TypeOf函数获取其动态类型信息,返回一个Type类型值。调用ValueOf函数获取该变量运行时的数据,返回一个Value类型值。
func TypeOf(i interface{}) Type
1 // 1. 普通类型 2 var temp int 3 typeA := reflect.TypeOf(temp) 4 fmt.Println(typeA) //int 5 fmt.Println(typeA.Name()) //int,返回表示类型名称的字符串 6 fmt.Println(typeA.Kind()) //int,返回 reflect.Kind 类型的常量 7 // 2. 指针类型 8 type Repo struct{ 9 name string `json:"omitempty"` 10 volume int 11 } 12 13 //创建一个Student实例指针 14 res := &Repo{"repo1", 10000} 15 typeRes := reflect.TypeOf(res) 16 17 fmt.Println(typeRes.Name()) //"" 18 fmt.Println(typeRes.Kind()) //ptr 19 20 //获取该指针的元素 [ValueOf 和Value.Elem的区别] 21 resElem := typeRes.Elem() 22 23 fmt.Println(resElem.Name()) //Repo 24 fmt.Println(resElem.Kind()) //struc 25 // 3. 结构体类型(NumField,Field & tag) 26 t := Repo{"repo2", 20000} // 传入指针类型,编译报错:panic: reflect: NumField of non-struct type 27 for i :=0; i < t.NumField(); i++ { 28 fieldType := t.Field(i) // 获取指定索引结构成员的信息 29 // t.FieldByName("name") 通过字段名,找到字段的类型信息 30 fmt.Println(fieldType.Name, fieldType.Tag) // name, json:"omit-empty" 31 fmt.Println(fieldType.Tag.Get("json") // omit-empty 使用Get()获取标签的value 32 }View Code
func ValueOf(i interface{}) Value [不仅能和上面一样获取值的类型(Type)信息,还可以动态地获取或设置变量的值]
1 // 1.普通用法 2 a := 1 //声明并赋值得到一个int类型的a 3 4 //获取其的反射值对象 5 valueOfA := reflect.ValueOf(a) 6 fmt.Println(valueOfA) //1 7 //获取该值的类型,注意elem.(type)这种方法只能用在switch中 8 fmt.Println(reflect.TypeOf(valueOfA)) //reflect.Value 9 10 //调用Interface()方法获取interface{}类型的值,然后使用类型断言进行转换成int类型 11 changedA1 := valueOfA.Interface().(int) 12 fmt.Println(changedA1) //1 13 fmt.Println(reflect.TypeOf(changedA1)) //int 14 15 //还有另一种类似的方法,就是调用Int()方法将其先转换成int64类型,然后再转成int类型 16 changedA2 := int(valueOfA.Int()) 17 fmt.Println(changedA2) //1 18 fmt.Println(reflect.TypeOf(changedA2))//int 19 // 2.结构体(NumField, Field) 20 type User struct { 21 Id int 22 Name string 23 Age int 24 } 25 user := User{1,"Tom",12} 26 v := reflect.ValueOf(user) 27 for i :=0; i < v.NumField(); i++ { 28 val := v.Field(i).Interface() // 获取指定结构成员的值 29 } 30 31 for i :=0; i <v.NumMethod(); i++ { 32 m := v.Method(i) // 获取指定索引的方法的信息 33 fmt.Printf("%6s: %v\n", m.Name, m.Type) // 打印方法的名称,方法声明 34 }View Code
二、判断反射值
func (v Value) IsNil() bool 判断值是否为nil,常用于判断指针是否为空
func (v Value) IsValid() bool 如果v是Value零值会返回false,此时v除了IsValid、String、Kind之外的方法都会导致panic。绝大多数函数和方法都永远不返回Value零值。
func (v Value) IsZero() boo 判断v是否是其类型的零值
三、修改反射值
func (v Value) Elem() Value Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装,类似于*操作,此时的Value表示的是Value的元素且可以寻址
func (v Value) SetInt(x int64) 设置v的持有值。如果v的Kind不是Int、Int8、Int16、Int32、Int64之一或者v.CanSet()返回假,会panic。