
标准库reflect
1.reflect简介
一个接口类型的变量存储了2个信息,值和类型,reflect.TYpeOf()和reflect.VlueOf(),返回被检查对象的类型和值。
当调用reflect.TypeOf(x)时,x被当作一个接口变量来处理,然后reflect.TypeOf()对接口变量进行拆解,返回其反射类型信息
- reflect.Type和reflect.Value还有许多其他检查和操作方法
Type的主要方法有:
Kind()返回一个常量,表示具体类型的底层类型
Elem()返回指针、数组、切片、字典、通道等类型。这个方法要慎用,如果用于其他类型会出现运行时异常
Value的主要方法有:
Kind()返回一个常量,表示具体类型的底层类型、
Type()返回具体类型所对应的reflect.Type(静态类型)
package main import ( "fmt" "reflect" ) type Student struct { name string } type MyInt int func main() { var a int = 9 v := reflect.ValueOf(a) // 返回Value类型对象,值为9 t := reflect.TypeOf(a) // 返回Type类型对象,值为int fmt.Println(v, t, v.Type(), v.Kind(), t.Kind()) // kind()返回底层基础类型 var mi MyInt = 99 mv := reflect.ValueOf(mi) // 返回Value类型对象,值为99 mt := reflect.TypeOf(mi) // 返回Type类型对象,值为MyInt fmt.Println(mv, mt, mv.Type(), mv.Kind(), mt.Kind()) // kind()返回底层基础类型 var b [5]int = [5]int{5, 6, 7, 8} fmt.Println(reflect.TypeOf(b), reflect.TypeOf(b).Kind(), reflect.TypeOf(b).Elem()) // [5]int array int var Pupil Student p := reflect.ValueOf(Pupil) // 使用ValueOf()获得结构体的Value对象 fmt.Println(p.Type()) // 输出:Student fmt.Println(p.Kind()) // 输出:struct }reflect.Type对应的值,而kind()对应的是基础类型
Kind()大概会返回切片、字典、数组、指针、结构体、接口、字符串、函数、整型或其他基础类型
Type()返回的是静态类型,而Kind()返回的是基础类型
2.反射的应用
- 通过反射可以修改对象
- 通过反射可以创建基础类型和用户自定义类型变量
3.通过反射可以修改对象
对象必须是可寻址,实际上要修改的是指针指向的数据,需要调用Value类型的Elem()方法。Elem()方法能够对指针进行间接引用,将结果存储到reflect.Value类型对象中
v := reflect.Value.Elem() 表示获取原始值对应的反射对象通过CanSet()方法来判断原始反射对象v reflect.Value是否可写,CanAddr()方法判断它是否可被取地址,这里的v是通过Elem()得到的。CanSet()和CanAddr()这两个方法的签名如下
func (v Value) CanAddr() func (v Value) CanSet() bool示例来访问、修改值
package main import ( "fmt" "reflect" ) type Student struct { name string Age int } func main() { var a int = 99 v := reflect.ValueOf(a) // 返回Value类型对象,值为9 t := reflect.TypeOf(a) // 返回Type类型对象,值为int fmt.Println(v.Type(), t.Kind(), reflect.ValueOf(&a).Elem()) fmt.Println(reflect.ValueOf(a).CanSet(), reflect.ValueOf(a).CanAddr()) fmt.Println(reflect.ValueOf(&a).CanSet(), reflect.ValueOf(&a).CanAddr()) pa := reflect.ValueOf(&a).Elem() // reflect.Value.Elem()表示获取原始值对应的反射对象 fmt.Println("CanSet:", pa.CanSet(), "CanAddr:", pa.CanAddr()) //fmt.Println(reflect.ValueOf(a).Elem().CanSet(), reflect.ValueOf(a).Elem().CanAddr()) fmt.Println(pa, pa.CanSet()) pa.SetInt(100) fmt.Println(pa) var Pupil Student = Student{"Jim", 8} Pupilv := reflect.ValueOf(Pupil) // 使用ValueOf()获取结构体的Value对象 fmt.Println(Pupilv.Type()) // 输出:Student fmt.Println(Pupilv.Kind()) // 输出:struct p := reflect.ValueOf(&Pupil).Elem() // 获取原始值对应的反射对象 fmt.Println(reflect.ValueOf(&Pupil)) fmt.Println("CanSet:", p.CanSet(), "CanAddr:", p.CanAddr()) //p.Field(0).SetString("Mike") // 未导出字段,不能修改,会发生异常 p.Field(1).SetInt(10) fmt.Println(p) }要通过反射的方式来修改对象,重点是通过方法Elem()获取原始值对应的反射对象,一个结构体中只有被导出的字段才是可写的。
reflect.ValueOf(&a)得到的是原始变量a的指针地址,这个指针地址再通过Elem()方法得到反射对象。
如果在结构体中有tag标签,通过反射可获取结构体成员变量的tag信息,如下示例
package main import ( "fmt" "reflect" ) type Student struct { name string Age int `json:"years"` } func main() { var Pupil Student = Student{"joke", 18} setStudent := reflect.ValueOf(&Pupil).Elem() sSAge, _ := setStudent.Type().FieldByName("Age") fmt.Println(sSAge.Tag.Get("json")) // years }
4.通过反射可以创建基础类型和用户自定义类型变量
除了可以通过反射创建基础类型和用户自定义类型,还可以使用反射来创建切片、字典、通道,甚至包括函数类型。常见函数有:reflect.Makeslice(),reflect.Makemap()和reflect.Makechan()。
要想创建变量,需要首先确定类型,下面代码中根据reflect.Type(t)得到t的静态类型,接着使用reflect.New(vartype)生成了新变量。新变量通过方法Elem()获取的反射对象来设置变量值,最后使用Elem().interface()来反引用reflect的指针,得到新变量的值。
package main import ( "fmt" "reflect" ) func main() { t := 9 // 反射创建int 变量 varType := reflect.TypeOf(t) v1 := reflect.New(varType) v1.Elem().SetInt(1) varNew := v1.Elem().Interface() fmt.Printf("int Var: 指针:%d 值:%d\n", v1, varNew) // 反射创建map slice newSlice := make([]int, 5) newmap := make(map[string]int) sliceType := reflect.TypeOf(newSlice) mapType := reflect.TypeOf(newmap) // 创建新值 ReflectSlice := reflect.MakeSlice(sliceType, 5, 5) Reflectmap := reflect.MakeMap(mapType) // 使用新创建的变量 V := 99 SliceV := reflect.ValueOf(V) ReflectSlice = reflect.Append(ReflectSlice, SliceV) intSlice := ReflectSlice.Interface().([]int) fmt.Println("Slice:", intSlice) Key := "Rose" Value := 999 MapKey := reflect.ValueOf(Key) MapValue := reflect.ValueOf(Value) Reflectmap.SetMapIndex(MapKey, MapValue) mapStringInt := Reflectmap.Interface().(map[string]int) fmt.Println("Map:", mapStringInt) }通过反射机制,能对一个结构体类型的大致结构如方法、字段的情况有较为全面的了解,代码如下
package main import ( "fmt" "reflect" ) // 结构体 type ss struct { int string bool float64 } func (s ss) Method1(i int) string { return "结构体方法1" } func (s *ss) Method2(i int) string { return "结构体方法2" } var ( structValue = ss{ // 结构体 20, "结构体", false, 64.0, } ) func main() { // 反射结构体 fmt.Println("=========引用=========") v := reflect.ValueOf(&structValue) fmt.Println("String :", v.String()) // 反射值的字符串形式 fmt.Println("Type :", v.Type()) // 反射值的类型 fmt.Println("Kind :", v.Kind()) // 反射值的类别 fmt.Println("CanAddr :", v.CanAddr()) // 是否可以获取地址 fmt.Println("CanSet :", v.CanSet()) // 是否可以修改 if v.CanAddr() { fmt.Println("Addr :", v.Addr()) // 获取地址 fmt.Println("UnsafeAddr :", v.UnsafeAddr()) // 获取自由地址 } // 获取方法数量 fmt.Println("可用方法数量 :", v.NumMethod()) if v.NumMethod() > 0 { i := 0 for ; i < v.NumMethod()-1; i++ { fmt.Printf(" ┣ %v\n", v.Method(i).String()) } fmt.Printf(" ┗ %v\n", v.Method(i).String()) // 通过名称获取方法 fmt.Println("Method1 MethodByName:", v.MethodByName("Method1").String()) fmt.Println("Method2 MethodByName:", v.MethodByName("Method2").String()) } fmt.Println("=========值变量=========") v = reflect.ValueOf(structValue) fmt.Println("String :", v.String()) // 反射值的字符串形式 fmt.Println("Type :", v.Type()) // 反射值的类型 fmt.Println("Kind :", v.Kind()) // 反射值的类别 fmt.Println("CanAddr :", v.CanAddr()) // 是否可以获取地址 fmt.Println("CanSet :", v.CanSet()) // 是否可以修改 if v.CanAddr() { fmt.Println("Addr :", v.Addr()) // 获取地址 fmt.Println("UnsafeAddr :", v.UnsafeAddr()) // 获取自由地址 } // 获取方法数量 fmt.Println("方法数量 :", v.NumMethod()) fmt.Println("指针变量可用方法数量 :", reflect.ValueOf(&structValue).NumMethod()) if v.NumMethod() > 0 { i := 0 for ; i < v.NumMethod()-1; i++ { fmt.Printf(" ┣ %v\n", v.Method(i).String()) } fmt.Printf(" ┗ %v\n", v.Method(i).String()) // 通过名称获取方法 fmt.Println("Method1 MethodByName:", v.MethodByName("Method1").String()) fmt.Println("Method2 MethodByName:", v.MethodByName("Method2").String()) } switch v.Kind() { // 结构体: case reflect.Struct: fmt.Println("=== 结构体 ===") // 获取字段个数 fmt.Println("NumField :", v.NumField()) if v.NumField() > 0 { var i int // 遍历结构体字段 for i = 0; i < v.NumField()-1; i++ { field := v.Field(i) // 获取结构体字段 fmt.Printf(" ├ %-8v %v\n", field.Type(), field.String()) } field := v.Field(i) // 获取结构体字段 fmt.Printf(" └ %-8v %v\n", field.Type(), field.String()) // 通过名称查找字段 if v := v.FieldByName("ptr"); v.IsValid() { fmt.Println("FieldByName(ptr) :", v.Type().Name()) } // 通过函数查找字段 v := v.FieldByNameFunc(func(s string) bool { return len(s) > 3 }) if v.IsValid() { fmt.Println("FieldByNameFunc :", v.Type().Name()) } } } }程序输出:
=========引用========= String : <*main.ss Value> Type : *main.ss Kind : ptr CanAddr : false CanSet : false 可用方法数量 : 2 ┣ <func(int) string Value> ┗ <func(int) string Value> Method1 MethodByName: <func(int) string Value> Method2 MethodByName: <func(int) string Value> =========值变量========= String : <main.ss Value> Type : main.ss Kind : struct CanAddr : false CanSet : false 方法数量 : 1 指针变量可用方法数量 : 2 ┗ <func(int) string Value> Method1 MethodByName: <func(int) string Value> Method2 MethodByName: <invalid Value> === 结构体 === NumField : 4 ├ int <int Value> ├ string 结构体 ├ bool <bool Value> └ float64 <float64 Value>