反射允许程序运行时对程序本身进行访问和修改的能力。反射主要是空接口(interface{})存储数据上,空接口(interface{})可以表达任意类型的数据,那我们如何知道这个空接口保存的数据值和数据类型了?反射就是在运行时动态的获取一个变量的类型和值。
在Go的反射定义中,任何接口都会由两部分组成的,一个是接口的具体类型,一个是具体类型对应的值。Golang中的reflect包实现了运行时反射,通过调用TypeOf函数返回一个Type类型值,该值代表运行时的数据类型,调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。

TypeOf

使用reflect.TypeOf()函数可以获得任意值的类型对象(reflect.Type),程序通过类型对象可以访问任意值的类型信息。TypeOf(nil)会返回nil。

  1. func TypeOf(i interface{}) Type

eg:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Foo struct {
  7. Bar string
  8. }
  9. func main() {
  10. var s ="hello world"
  11. fmt.Println(reflect.TypeOf(s))
  12. a := 3.14
  13. fmt.Println(reflect.TypeOf(a))
  14. b := 2
  15. fmt.Println(reflect.TypeOf(b))
  16. f := Foo{Bar:"bar"}
  17. fmt.Println(reflect.TypeOf(f))
  18. }

打印结果

  1. string
  2. float64
  3. int
  4. main.Foo

通用方法

  1. func (t *rtype) String() string // 获取 t 类型的字符串描述,不要通过 String 来判断两种类型是否一致。
  2. func (t *rtype) Name() string // 获取 t 类型在其包中定义的名称,未命名类型则返回空字符串。
  3. func (t *rtype) PkgPath() string // 获取 t 类型所在包的名称,未命名类型则返回空字符串。
  4. func (t *rtype) Kind() reflect.Kind // 获取 t 类型的类别。
  5. func (t *rtype) Size() uintptr // 获取 t 类型的值在分配内存时的大小,功能和 unsafe.SizeOf 一样。
  6. func (t *rtype) Align() int // 获取 t 类型的值在分配内存时的字节对齐值。
  7. func (t *rtype) FieldAlign() int // 获取 t 类型的值作为结构体字段时的字节对齐值。
  8. func (t *rtype) NumMethod() int // 获取 t 类型的方法数量。
  9. func (t *rtype) NumField() int //返回一个struct 类型 的属性个数,如果非struct类型会抛异常
  10. func (t *rtype) Method() reflect.Method // 根据索引获取 t 类型的方法,如果方法不存在,则 panic。
  11. // 如果 t 是一个实际的类型,则返回值的 Type 和 Func 字段会列出接收者。
  12. // 如果 t 只是一个接口,则返回值的 Type 不列出接收者,Func 为空值。
  13. func (t *rtype) MethodByName(string) (reflect.Method, bool) // 根据名称获取 t 类型的方法。
  14. func (t *rtype) Implements(u reflect.Type) bool // 判断 t 类型是否实现了 u 接口。
  15. func (t *rtype) ConvertibleTo(u reflect.Type) bool // 判断 t 类型的值可否转换为 u 类型。
  16. func (t *rtype) AssignableTo(u reflect.Type) bool // 判断 t 类型的值可否赋值给 u 类型。
  17. func (t *rtype) Comparable() bool // 判断 t 类型的值可否进行比较操作
  18. //注意对于:数组、切片、映射、通道、指针、接口
  19. func (t *rtype) Elem() reflect.Type // 获取元素类型、获取指针所指对象类型,获取接口的动态类型

Kind

Value.Type()Value.Kind()这两个方法都可以获取对象或者变量的类型,如果是变量的话,使用这两个方法获取到的类型都是一样,差别是结构体对象。s’q下面的例子中f 实际类型是Foo,对应的底层数据类型是struct结构体类型,通过种Kind方法就可以获取到底层的类型,特别是当需要区分指针、结构体等大品种的类型时,kind 的方式就显得尤为重要。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Foo struct {
  7. Bar string
  8. }
  9. func reflectType(x interface{}) {
  10. t := reflect.TypeOf(x)
  11. fmt.Printf("type:%v,name:%v,kind:%v\n",t, t.Name(), t.Kind())
  12. }
  13. func main() {
  14. f := Foo{Bar:"bar"}
  15. reflectType(f)
  16. reflectType(&f)
  17. list := []string{"q","w"}
  18. reflectType(list)
  19. m:= make(map[string]string)
  20. m["a"]= "a"
  21. reflectType(m)
  22. }

注意Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回。上面语句执行结果

  1. type:main.Foo,name:Foo,kind:struct
  2. type:*main.Foo,name:,kind:ptr
  3. type:[]string,name:,kind:slice
  4. type:map[string]string,name:,kind:map

reflect包中定义的Kind类型如下:

  1. type Kind uint
  2. const (
  3. Invalid Kind = iota // 非法类型
  4. Bool // 布尔型
  5. Int // 有符号整型
  6. Int8 // 有符号8位整型
  7. Int16 // 有符号16位整型
  8. Int32 // 有符号32位整型
  9. Int64 // 有符号64位整型
  10. Uint // 无符号整型
  11. Uint8 // 无符号8位整型
  12. Uint16 // 无符号16位整型
  13. Uint32 // 无符号32位整型
  14. Uint64 // 无符号64位整型
  15. Uintptr // 指针
  16. Float32 // 单精度浮点数
  17. Float64 // 双精度浮点数
  18. Complex64 // 64位复数类型
  19. Complex128 // 128位复数类型
  20. Array // 数组
  21. Chan // 通道
  22. Func // 函数
  23. Interface // 接口
  24. Map // 映射
  25. Ptr // 指针
  26. Slice // 切片
  27. String // 字符串
  28. Struct // 结构体
  29. UnsafePointer // 底层指针
  30. )

Elem()

获取元素类型、获取指针所指对象类型,获取接口的动态类型。
eg:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type UserInfo struct {
  7. Name string
  8. Age int
  9. }
  10. func main() {
  11. u := &UserInfo{
  12. Name: "ming",
  13. Age: 18,
  14. }
  15. userType := reflect.TypeOf(u)
  16. fmt.Println(userType.Kind(), userType.Name())
  17. elem := userType.Elem()
  18. fmt.Println(elem.Kind(), elem.Name())
  19. }

可以通过reflect.Elem()获取这个指针指向元素的类型,

  1. ptr
  2. struct UserInfo

ValueOf

ValueOf返回一个初始化为i接口保管的具体值的Value,ValueOf(nil)返回Value零值。

  1. func ValueOf(i interface{}) Value

获取变量的值:

  1. reflect.ValueOf(x).Int()
  2. reflect.ValueOf(x).Float()
  3. reflect.ValueOf(x).String()
  4. reflect.ValueOf(x).Bool()

eg:

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Foo struct {
  7. Bar string
  8. }
  9. func reflectValue(i interface{}){
  10. v :=reflect.ValueOf(i)
  11. k :=v.Kind()
  12. switch k {
  13. case reflect.Int:
  14. fmt.Printf("type is int value is %d\n",v.Int())
  15. case reflect.String:
  16. fmt.Printf("type is string value is %s\n",v.String())
  17. default:
  18. fmt.Printf("type is %v,value is %v\n",k,v)
  19. }
  20. }
  21. func main() {
  22. a := 10
  23. reflectValue(a)
  24. s := "hello"
  25. reflectValue(s)
  26. f := Foo{Bar:"bar"}
  27. reflectValue(f)
  28. }

打印结果

  1. type is int value is 10
  2. type is string value is hello
  3. type is struct,value is {bar}

修改值

通过反射的来改变变量的值reflect.Value.SetXX相关方法

  1. reflect.Value.SetInt(),//设置整数
  2. reflect.Value.SetFloat(),//设置浮点数
  3. reflect.Value.SetString(),//设置字符串

SetXX(x) 因为传递的是 x 的值的副本,所以SetXX不能够改 x,否则会引起panic

  1. func main() {
  2. var a = 2
  3. v := reflect.ValueOf(a)
  4. v.SetInt(3)
  5. fmt.Println(a)
  6. }

执行上面程序会出现下面错误

  1. panic: reflect: reflect.flag.mustBeAssignable using unaddressable value

通过反射修改值的步骤如下:
1.reflect.ValueOf函数返回的是一份值的拷贝,所以前提是我们是传入要修改变量的地址,也就是必须向函数传递 x 的指针,SetXX(&x) 。
2 获取指针的值需要通过Elem函数

  1. func main() {
  2. var a = 2
  3. v := reflect.ValueOf(&a)
  4. v.Elem().SetInt(3)
  5. fmt.Println(a)
  6. }

Indirect

取指针里面的值,和Elem性质差不多, 也是修改值

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. var i =1
  8. value :=reflect.ValueOf(&i)
  9. value = reflect.Indirect(value)
  10. fmt.Println(value.Interface())
  11. if value.Kind() == reflect.Int{
  12. value.SetInt(2)
  13. }
  14. fmt.Println(value.Interface())
  15. }

上面打印结果

  1. 1
  2. 2

修改结构体

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Bar struct {
  7. Foo string
  8. }
  9. func main() {
  10. b:=Bar{"hello"}
  11. value := reflect.ValueOf(&b)
  12. value = reflect.Indirect(value)
  13. fmt.Println(value.Interface())
  14. f:= value.FieldByName("Foo")
  15. if f.Kind()==reflect.String&&f.CanSet(){
  16. f.SetString("world")
  17. }
  18. fmt.Println(f)
  19. fmt.Println(value.Interface())
  20. }

IsNil

IsNil()主要被用于判断指针是否为空。v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;否则IsNil函数会导致panic,下面是IsNil的源码

  1. func (v Value) IsNil() bool {
  2. k := v.kind()
  3. switch k {
  4. case Chan, Func, Map, Ptr, UnsafePointer:
  5. if v.flag&flagMethod != 0 {
  6. return false
  7. }
  8. ptr := v.ptr
  9. if v.flag&flagIndir != 0 {
  10. ptr = *(*unsafe.Pointer)(ptr)
  11. }
  12. return ptr == nil
  13. case Interface, Slice:
  14. // Both interface and slice are nil if first word is 0.
  15. // Both are always bigger than a word; assume flagIndir.
  16. return *(*unsafe.Pointer)(v.ptr) == nil
  17. }
  18. panic(&ValueError{"reflect.Value.IsNil", v.kind()})
  19. }
  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Foo struct {
  7. Bar string
  8. }
  9. func main() {
  10. var s *string
  11. fmt.Println(reflect.ValueOf(s).IsNil())//true
  12. f := Foo{Bar:"bar"}
  13. fmt.Println(reflect.ValueOf(&f).IsNil())//false
  14. }

IsValid

IsValid()常被用于判定返回值是否有效

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Foo struct {
  7. Bar string
  8. }
  9. func main() {
  10. fmt.Println(reflect.ValueOf(nil).IsValid())
  11. var s *string
  12. fmt.Println(reflect.ValueOf(s).IsValid())
  13. f :=Foo{Bar:"bar"}
  14. fmt.Println(reflect.ValueOf(f).FieldByName("Bar").IsValid())
  15. fmt.Println(reflect.ValueOf(f).MethodByName("Bar").IsValid())
  16. }

执行结果

  1. false
  2. true
  3. true
  4. false

反射应用

成员变量

方法 说明
Field(i int) StructField 根据索引,返回索引对应的结构体字段的信息。
NumField() int 返回结构体成员字段数量。
FieldByName(name string) (StructField, bool) 根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool) 根据传入的匹配函数匹配需要的字段。

StructField 结构函数

  1. ype StructField struct {
  2. Name string // 字段名
  3. PkgPath string // 字段路径
  4. Type Type // 字段反射类型对象
  5. Tag StructTag // 字段的结构体标签
  6. Offset uintptr // 字段在结构体中的相对偏移
  7. Index []int // Type.FieldByIndex中的返回的索引值
  8. Anonymous bool // 是否为匿名字段
  9. }

eg

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type UserInfo struct {
  7. Name string
  8. Age int
  9. }
  10. func main() {
  11. u := &UserInfo{}
  12. userType := reflect.TypeOf(u)
  13. elem := reflect.New(userType.Elem()).Elem()
  14. elem.FieldByName("Name").SetString("tony")
  15. elem.FieldByName("Age").SetInt(20)
  16. fmt.Println(elem.Field(0),elem.Field(1))
  17. }

打印结果

  1. tony 20

字段遍历

Name是字段的名字。
PkgPath是非导出字段的包路径,对导出字段该字段为””

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type Student struct {
  7. Name string `orm:"name"`
  8. Age int `orm:"age"`
  9. }
  10. func (s *Student)Update(){
  11. s.Age+=1
  12. }
  13. func (s Student)Show(){
  14. fmt.Println(s.Name,s.Age)
  15. }
  16. func (s Student)print(){
  17. fmt.Println(s.Name,s.Age)
  18. }
  19. func main() {
  20. s := Student{Name:"BX",Age:18}
  21. t :=reflect.TypeOf(s)
  22. for i:=0;i<t.NumField();i++{
  23. field := t.Field(i)
  24. fmt.Printf("name=%s PkgPath=%s index=%d type=%v tag=%v Offset=%v Anonymous=%v\n", field.Name,field.PkgPath, field.Index, field.Type, field.Tag.Get("orm"),field.Offset,field.Anonymous)
  25. }
  26. p :=reflect.TypeOf(&s)
  27. for i :=0;i<p.NumMethod();i++{
  28. method := p.Method(i)
  29. fmt.Printf("name=%s PkgPath=%s index=%d type=%v func=%v \n", method.Name,method.PkgPath, method.Index, method.Type, method.Func)
  30. }
  31. }

执行结果

动态创建对象

动态调用方法

方法 说明
NumMethod() int 返回该类型的方法集中方法的数目
Method(int) Method 返回该类型方法集中的第i个方法
MethodByName(string)(Method, bool) 根据方法名返回该类型方法集中的方法

通过MethodByName来进行方法的调用

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type User struct {
  7. name string
  8. age int
  9. }
  10. func (u *User)update(name string,age int){
  11. u.name = name
  12. u.age = age
  13. }
  14. func(u *User)Show(){
  15. fmt.Printf("name=%s,age=%d\n",u.name,u.age)
  16. }
  17. func main() {
  18. u := User{
  19. name: "tony",
  20. age: 10,
  21. }
  22. v := reflect.ValueOf(&u)
  23. name := reflect.ValueOf(&u.name)
  24. name.Elem().SetString("wang")
  25. update := v.MethodByName("update")
  26. if update.IsValid() {
  27. args :=[]reflect.Value{reflect.ValueOf("ellen"),reflect.ValueOf(20)}
  28. update.Call(args)
  29. }
  30. show := v.MethodByName("Show")
  31. if show.IsValid() {
  32. show.Call([]reflect.Value{})
  33. }
  34. }

执行结果

  1. name=wang,age=10

Tag标签

结构体中的字段除了有名字和类型外,还可以有一个可选的标签。它是一个附属于字段的字符串,可以是文档或其它的重要标记。标签的内容不可以在一般的编程中使用,只有包reflect能获取它。reflect包可以在运行时自省类型、属性和方法,比如在一个变量上调用reflect.TypeOf()可以获取变量的正确类型,如果变量是一个结构体类型,就可以通过Field来索引结构体的字段,然后就可以使用Tag属性

  1. package main
  2. import (
  3. "reflect"
  4. "fmt"
  5. )
  6. type User struct { // tags
  7. Id int64 `json:"id"`
  8. Name string `json:"name"`
  9. Gender bool `json:"gender"`
  10. }
  11. func main() {
  12. u := User{10001, "ming", true}
  13. t := reflect.TypeOf(u)
  14. v := reflect.ValueOf(u)
  15. for i := 0; i < t.NumField(); i++ {
  16. fmt.Printf("%v=%v\n", t.Field(i).Tag.Get("json"),v.Field(i).Interface())
  17. }
  18. }

执行结果

  1. id=10001
  2. name=ming
  3. gender=true

接口判断

Implements(u Type) bool // 判断是否存在与 u 相同的接口

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. //判断实例是否实现了某接口
  7. type Foo interface {
  8. show()
  9. }
  10. type Bar struct {
  11. }
  12. func (b *Bar) show() {
  13. fmt.Println("hello world")
  14. }
  15. func main() {
  16. b := new(Bar)
  17. f := reflect.TypeOf((*Foo)(nil)).Elem()
  18. tt := reflect.TypeOf(b)
  19. res := tt.Implements(f)
  20. fmt.Println(res)
  21. }