反射是值在程序运行期间对程序本身进行访问和修改的能力. 程序在编译时, 变量被转换为内存地址, 程序运行时, 程序无法获取自身的信息.
支持反射的语言可以在程序编译期将变量的反射信息, 如字段名称, 类型信息, 结构体信息等整合到可执行文件中, 并给程序提供接口访问反射信息, 这样就可以在程序运行期获取类型的反射信息, 并且有能力修改它们.
任意接口值在反射中都可以理解为由 Type 和 Value 组成的. Go 语言的反射是由 reflect 包提供的, 它定义了两个重要的类型 reflect.Type 和 reflect.Value, 并提供了 reflect.TypeOf 和 reflect.ValueOf 函数来获取任意对象的 Value 和 Type.
反射的类型接口 reflect.Type 在 Go 语言程序中, 使用 reflect.TypeOf() 函数可以获得任意值的 reflect.Type 接口对象, 程序通过类型对象可以访问对象的类型信息.
1 2 3 4 5 6 7 8 9 10 import  (    "fmt"      "reflect"  ) func  main ()   {    var  n int  = 20      typeOfn := reflect.TypeOf(n)       fmt.Println("type:" , typeOfn.Name(), "kind:" , typeOfn.Kind())   } 
 
类型名称(Name)与种类(Kind) 
Name(类型名称) 返回反射类型的的名称, 包括 Go 语言中原生数据类型及通过 type 关键字自定义的数据类型. 获取方式为 reflect.Type 的 Name() 方法 
Kind(种类) 指反射类型所属的种类, 它仅包含 Go 语言中原生数据种类. 获取方式为 reflect.Type 的 Kind() 方法, 返回 reflect.Kind 类型的常量 
 
在如下示例中, stu 对象的反射类型名称为自定义的 Student, 而其所属的反射种类为 struct.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  (    "fmt"      "reflect"  ) type  Student struct  {    Name  string      Age   int  } func  main ()   {    stu := Student{         Name:  "tom" ,         Age:   20 ,     }     typeOfstu := reflect.TypeOf(stu)     fmt.Println("type:" , typeOfstu.Name(), "kind:" , typeOfstu.Kind()) } 
 
特殊反射种类的类型名称 获取反射种类(Kind)为 Array, Chan, Map, Ptr 或 Slice 的对象的反射类型名称时, 通过 Name() 方法获得的类型名称为空字符串(“”)
要想获取以上对象的反射类型名称, 需要通过 Elem() 方法获取反射类型的元素类型, 然后再通过 Name() 方法获取其反射类型的名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import  (    "fmt"      "reflect"  ) type  Student struct  {    Name  string      Age   int  } func  main ()   {    stu := Student{        Name:  "tom" ,        Age:   20 ,     }     ptrOfstu := &stu     typeOfptr := reflect.TypeOf(ptrOfstu)       fmt.Println("type:" , typeOfptr.Name(), "kind:" , typeOfptr.Kind())       elem := typeOfptr.Elem()     fmt.Println("elem's type:" , elem.Name(), "elem's kind:" , elem.Kind())   } 
 
reflect.Type 的 Elem() 方法仅当反射类型的种类是 Array, Chan, Map, Ptr 或 Slice 时才可以获取其元素类型, 否则 Elem() 方法会引发 panics.
使用反射获取结构体的成员及方法信息 通过 reflect.TypeOf() 获得反射对象信息后, 如果它所属种类是结构体,可通过 reflect.Type 的 NumField() 和 Field() 方法获得结构体成员的详细信息, 可通过NumMethod() 和 Method() 方法获取结构体的方法详细信息
reflect.Type 中与成员及方法获取相关的方法如下表所示
方法 
参数 
返回值 
说明 
 
 
NumField() 
- 
int 
返回结构体成员字段数量 
 
Field(i int) 
字段索引,从 0 开始 
reflect.StructField 
根据索引返回索引对应的结构体字段的信息 
 
NumMethod() 
- 
int 
返回结构体方法数量 
 
Method(i int) 
字段索引,从 0 开始 
reflect.Method 
根据索引返回索引对应的结构体方法的信息, 方法的排序是根据 ascii 码的先后顺序进行排序的 
 
需要注意的是, 方法相关信息可直接通过 reflect.TypeOf(&object).Method() 方法获取, 且通过这种方法获取的方法包括 receiver 为 &object 的方法. reflect.TypeOf(object).Method() 仅返回 receiver 为 object 的方法. 而字段相关信息只能通过 Elem() 获取元素类型后获取字段相关信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import  (    "fmt"      "reflect"  ) type  Student struct  {    Name  string   `json: "name"`      Age   int      `json: "age"`  } func  (stu Student)  GetSum (n1, n2 int )  int   {    return  n1 + n2 } func  (stu *Student)  Print ()   {    fmt.Println(*stu) } func  (stu *Student)  Set (name string , age int )   {    stu.Name = name     stu.Age = age } func  main ()   {    stu := Student{         Name: "tom" ,         Age:  20 ,     }     typeOfstu := reflect.TypeOf(stu)     kindOfstu := typeOfstu.Kind()     typeOfptr := reflect.TypeOf(&stu)     if  kindOfstu != reflect.Struct {         fmt.Println("except struct..." )         return      }     fieldNum := typeOfstu.NumField()     fmt.Println("字段个数为: " , fieldNum)       for  i := 0 ; i < fieldNum; i++ {         field := typeOfstu.Field(i)         fmt.Println(field)         fmt.Printf("字段名: %v, 字段类型: %v, 字段的 json 标签: %v\n" , field.Name, field.Type, field.Tag.Get("json" ))       }     methodNumOfstu := typeOfstu.NumMethod()     fmt.Println("stu方法个数为: " , methodNumOfstu)       for  i := 0 ; i < methodNumOfstu; i++ {         method := typeOfstu.Method(i)         fmt.Println(method)         fmt.Printf("方法名: %v, 方法类型: %v\n" , method.Name, method.Type)     }     methodNumOfptr := typeOfptr.NumMethod()     fmt.Println("&stu方法个数为: " , methodNumOfptr)        for  i := 0 ; i < methodNumOfptr; i++ {         method := typeOfptr.Method(i)         fmt.Println(method)         fmt.Printf("方法名: %v, 方法类型: %v, 方法对象%v\n" , method.Name, method.Type, method.Func)     } } 
 
反射的值对象 reflect.Value 使用 reflect.ValueOf() 函数可以获得对象的反射值对象 reflect.Value. reflect.Value 对象可以通过调用 Type() 方法返回 reflect.Type 对象, 完成值到类型对象的转换.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  (    "fmt"      "reflect"  ) func  main ()   {    var  n int  = 20      valueOfN := reflect.ValueOf(n)       typeOfN := valueOfN.Type()      fmt.Printf("值为: %v, 实际类型为 %T" , valueOfN, valueOfN)       n2, ok := valueOfN.Interface().(int )      if  ok {         fmt.Printf("转换后值为: %v, 类型为 %T" , n2, n2)       } } 
 
通过以上可以看得出, 实际类型对象将自身通过接口方式传入 reflect.ValueOf(i interface{}) 方法, 返回 reflect.Value 对象. reflect.Value 对象通过调用自身 Interface() 方法, 返回接口类型, 该接口类型可通过类型断言转化为实际类型对象.
使用反射获取结构体的成员值及方法信息 通过 reflect.ValueOf() 获得反射对象值信息后, 如果它所属种类是结构体,可通过 reflect.Value 的 NumField() 和 Field() 方法获得结构体成员的值信息, 可通过NumMethod() 和 Method() 方法获取结构体的方法等信息
reflect.Value 中与成员及方法获取相关的方法如下表所示:
方法 
参数 
返回值 
说明 
 
 
NumField() 
- 
int 
返回结构体成员字段数量 
 
Field(i int) 
字段索引,从 0 开始 
reflect.Value 
根据索引返回结构体字段对应的值对象 
 
NumMethod() 
- 
int 
返回结构体方法数量 
 
Method(i int) 
字段索引,从 0 开始 
reflect.Value 
根据索引返回结构体方法对应的值对象, 输出为内存地址(尚不清楚是什么地址), 方法的排序是根据 ascii 码的先后顺序进行排序的 
 
需要注意的是, 方法相关信息可直接通过 reflect.ValueOf(&object).Method() 方法获取, 且通过这种方法获取的方法包括 receiver 为 &object 的方法. reflect.ValueOf(object).Method() 仅返回 receiver 为 object 的方法. 而字段值对象相关信息只能通过 Elem() 获取值元素后获取字段段值对象相关信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import  (    "fmt"      "reflect"  ) type  Student struct  {    Name  string   `json: "name"`      Age   int      `json: "age"`  } func  (stu Student)  GetSum (n1, n2 int )  int   {    return  n1 + n2 } func  (stu *Student)  Print ()   {    fmt.Println(*stu) } func  (stu *Student)  Set (name string , age int )   {    stu.Name = name     stu.Age = age } func  main ()   {    stu := Student{         Name: "tom" ,         Age:  20 ,     }     valueOfstu := reflect.ValueOf(stu)     valueOfptr := reflect.ValueOf(&stu)     typeOfStu := valueOfstu.Type()      kindOfstu := valueOfstu.Kind()      if  kindOfstu != reflect.Struct {         fmt.Println("except struct..." )         return      }     fmt.Println(typeOfStu)     fieldNum := valueOfstu.NumField()     fmt.Println("字段个数为: " , fieldNum)      for  i := 0 ; i < fieldNum; i++ {         field := valueOfstu.Field(i)         fmt.Println(field)      }     methodNumOfstu := valueOfstu.NumMethod()     fmt.Println("stu方法个数为: " , methodNumOfstu)      for  i := 0 ; i < methodNumOfstu; i++ {         method := valueOfstu.Method(i)         fmt.Println(method)      }     methodNumOfptr := valueOfptr.NumMethod()     fmt.Println("&stu方法个数为: " , methodNumOfptr)      for  i := 0 ; i < methodNumOfptr; i++ {         method := valueOfptr.Method(i)         fmt.Println(method)      } } 
 
使用反射调用结构体的方法 继续看上一个例子, 通过反射如何调用 Student 中的方法呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import  (    "fmt"      "reflect"  ) type  Student struct  {    Name  string   `json: "name"`      Age   int      `json: "age"`  } func  (stu Student)  GetSum (n1, n2 int )  int   {    return  n1 + n2 } func  (stu *Student)  Print ()   {    fmt.Println(*stu) } func  (stu *Student)  Set (name string , age int )   {    stu.Name = name     stu.Age = age } func  main ()   {    stu := Student{         Name: "tom" ,         Age:  20 ,     }     valueOfStu := reflect.ValueOf(&stu)            var  paramsGetSum []reflect.Value     paramsGetSum = append (paramsGetSum, reflect.ValueOf(10 ))     paramsGetSum = append (paramsGetSum, reflect.ValueOf(20 ))     resGetSum := valueOfStu.Method(0 ).Call(paramsGetSum)       fmt.Printf("调用结果为 %v, 类型为 %T\n" , resGetSum, resGetSum)       resGetSum0 := resGetSum[0 ]     fmt.Printf("第一个返回值为 %v, 类型为 %T\n" , resGetSum0, resGetSum0)       realRes0 := resGetSum[0 ].Interface().(int )     fmt.Printf("实际结果为 %v, 类型为 %T\n" , realRes0, realRes0)            var  paramsPrint []reflect.Value     resPrint := valueOfStu.Method(1 ).Call(paramsPrint)     fmt.Printf("调用结果为 %v, 类型为 %T" , resPrint, resPrint)            var  paramsSet []reflect.Value     paramsSet = append (paramsSet, reflect.ValueOf("jack" ))     paramsSet = append (paramsSet, reflect.ValueOf(30 ))     resSet := valueOfStu.Method(2 ).Call(paramsSet)     fmt.Printf("调用结果为 %v, 类型为 %T\n" , resSet, resSet)      fmt.Println(stu)   }