变量的内在机制
  1. 类型信息,这部分是元信息,是预先定义好的
  2. 值类型,这部分是程序运行过程中,动态改变的

反射机制就是在运行时动态的调用对象的方法和属性,官方自带的reflect包就是反射相关的,只要包含这个包就可以使用。

多插一句,Golang的gRPC也是通过反射实现的。

反射与空接口

空接口可以存储任何类型的变量
那么给你一个空接口,怎么判断里面存储的是什么东西?
在运行时动态的获取一个变量的类型和值信息就叫反射

内置包: reflect
获取类型信息: reflect.TypeOf
获取值信息: reflect.ValueOf

反射种类(kind)定义

Go程序中的类型(Type)指的是系统原生数据类型,如int、string、bool、float32等类型,以及使用type关键字定义的类型,这些类型的名称就是其类型本身的名称。例如使用typeAstruct{}定义结构体时,A就是struct{}的类型。种类(Kind)指的是对象归属的品种,在reflect包中有如下定义

type Kind uint

const (
   Invalid Kind = iota  // 非法类型
   Bool                 // 布尔型
   Int                  // 有符号整型
   Int8                 // 有符号8位整型
   Int16                // 有符号16位整型
   Int32                // 有符号32位整型
   Int64                // 有符号64位整型
   Uint                 // 无符号整型
   Uint8                // 无符号8位整型
   Uint16               // 无符号16位整型
   Uint32               // 无符号32位整型
   Uint64               // 无符号64位整型
   Uintptr              // 指针
   Float32              // 单精度浮点数
   Float64              // 双精度浮点数
   Complex64            // 64位复数类型
   Complex128           // 128位复数类型
   Array                // 数组
   Chan                 // 通道
   Func                 // 函数
   Interface            // 接口
   Map                  // 映射
   Ptr                  // 指针
   Slice                // 切片
   String               // 字符串
   Struct               // 结构体
   UnsafePointer        // 底层指针
)

Map、Slice、Chan属于引用类型,使用起来类似于指针,但是在种类常量定义中仍然属于独立的种类,不属于Ptr。type A struct{}定义的结构体属于Struct种类,*A属于Ptr

TypeOf和ValueOf
func reflect_example(a interface{}) {
   t := reflect.TypeOf(a)
   fmt.Printf("type of a is %v\n", t)

   v := reflect.ValueOf(a)
   fmt.Printf("value of a is %v\n", v)

   k := t.Kind()
   fmt.Println(k)

}

func main() {
   var x int64 = 3
   reflect_example(x)
}
运行时修改值
func reflect_example(a interface{}) {
   v := reflect.ValueOf(a)
   k := v.Kind()
   switch k {
   case reflect.Int64:
      fmt.Printf("a is int64, store value is: %d\n", v.Int())
   case reflect.Float64:
      fmt.Printf("a is Float64, store value is: %f\n", v.Float())
   case reflect.Ptr:
      //指针类型 .Elem()相当于 指针取值
      v.Elem().SetFloat(22.5)
      fmt.Println("指针")
   default:
      fmt.Println("default")
   }
}

func main() {
   var x float64 = 3.4
   reflect_example(&x)
   fmt.Println(x)
}
var x float64 = 3.4
//这边要传地址,不然反射的是副本,下面修改副本的值会报错
v1 := reflect.ValueOf(&x)

//这边已经是指针,要用Elem,通过Elem()获取指针指向的变量,从而完成赋值操作
v1.Elem().SetFloat(4.3)
fmt.Println(v1.Elem().Float())
结构体属性
type Student struct {
   Name string
   Sex int
   Age int
   //abc string
}

func main() {
   var s Student
   v := reflect.ValueOf(s)
   t := v.Type()

   kind := t.Kind()
   fmt.Println(kind)  //struct

   //查看字段数量,包含私有字段
   fmt.Println(v.NumField())

   //注意,私有获取不到,会报错的
   for i := 0; i < v.NumField(); i++ {
      field := v.Field(i)
      fmt.Printf("名字: %s 类型: %v 值: %v\n", t.Field(i).Name, field.Type(), field.Interface())
   }

   //修改结构体内部的值
   v1 := reflect.ValueOf(&s)
   //用索引方式
   v1.Elem().Field(0).SetString("abc")
   //指定名称方式
   v1.Elem().FieldByName("Sex").SetInt(2)
   v1.Elem().FieldByName("Age").SetInt(12)

   fmt.Println(s)
}

输出

struct
3
名字: Name 类型: string 值: 
名字: Sex 类型: int 值: 0
名字: Age 类型: int 值: 0
{abc 2 12}
结构体方法
func (s *Student) Test() {
   fmt.Println("this is test")
}

func main() {
   s := Student{23, "skidoo"}
   v := reflect.ValueOf(&s)
   t := v.Type()
   v.Elem().Field(0).SetInt(100)
   fmt.Println("method num: ", v.NumMethod())
   for i := 0; i < v.NumMethod(); i++ {
      f := t.Method(i)
      fmt.Printf("%d method, name: %v, type: %v\n", i, f.Name, f.Type)
   }
}

输出

method num:  1
0 method, name: Test, type: func(*main.Student)
调用结构体方法
type Student struct {
   A int
   B string
}

func (s *Student) Test() {
   fmt.Println("this is test")
}

func (s *Student) SetA (a int) {
   s.A = a
}

func main() {
   s := Student{23, "skidoo"}
   //要引用传递,不然修改的是副本会报错
   v := reflect.ValueOf(&s)

   m := v.MethodByName("Test")
   var args1 []reflect.Value
   m.Call(args1)

   setA := v.MethodByName("SetA")
   var args2 []reflect.Value
   //参数
   args2 = append(args2, reflect.ValueOf(100))
   setA.Call(args2)
   fmt.Printf("s: %#v\n", s)
}

输出

this is test
s: main.Student{A:100, B:"skidoo"}
获取结构体中的tag信息
type Student struct {
   F string `species:"gopher" color:"blue" json:"f"`
}

func main() {
   s := Student{}
   //要引用传递,不然修改的是副本会报错
   v := reflect.TypeOf(s)
   field := v.Field(0)
   fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"), field.Tag.Get("json"))

复制

输出

blue gopher f