反射
反射是指在程序运行期间对程序本身进行访问和修改的能力,(程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时,程序无法获取自身的信息)
支持反射的语言可以在程序编译期间将变量的反射信息,如字段名称、类型等信息整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期间获取类型的反射信息,并且有能力修改它们
Go 程序在运行期间使用 reflect 包访问程序的反射信息
像 Python,JavaScript 类动态语言,由于本身的语法特性就可以让代码运行期间访问程序自身的值和类型信息,因此不需要反射系统
通过反射获取类型信息
使用 reflect.TypeOf() 函数可以获取变量的类型对象(reflect.Type)
通过类型对象可以获取自身的类型信息,使用 Name() 方法获取类型名称,使用 Kind() 方法获取类型归属的种类
package main import ( "fmt" "reflect" ) func GetReflectInfo(a interface{}) { // 获取变量 a 的类型对象,类型对象的类型是 reflect.Type var typeOfA reflect.Type = reflect.TypeOf(a) fmt.Printf("%T\n", typeOfA) // 打印类型名 和 种类 fmt.Println(typeOfA.Name(), typeOfA.Kind()) } func main() { GetReflectInfo("666") } 运行结果: *reflect.rtype string string
发现类型和种类都是 string,很奇怪是不是,接着看下面的例子 ...
理解反射的类型(Type)和种类(Kind)
编程中,使用最多的是类型,但在反射中,当需要区分一个大品种的类型时,就会用到种类(Kind)
1)类型(Type)
类型指的是系统原生数据类型,如 int、string、bool、float32 等类型,以及使用 type 关键字自定义的类型,自定义类型需要指定类型名称,例如,type A struct {},类型名就是 A
2)种类(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 种类
3)从自定义的类型对象中获取类型名称和类型种类(加强理解)
package main import ( "fmt" "reflect" ) type Cat struct{} type Enum int func main(){ var cat Cat = Cat{} var num Enum = 1 //对 cat 变量使用反射 var typeOfCat reflect.Type= reflect.TypeOf(cat) fmt.Println(typeOfCat.Name(), typeOfCat.Kind()) //对 num 变量使用反射 typeOfNum := reflect.TypeOf(num) fmt.Println(typeOfNum.Name(), typeOfNum.Kind()) } 运行结果: Cat struct Enum int