golang反射与反射三法则
反射是在golang程序运行时检查变量所具有类型的一种机制。由于反射可以得出关于变量结构的数据(即“关于数据的数据”),所以这也被认为是golang元编程的基础。初学反射,会感觉有些“玄乎”。我这里由浅入深,尝试阐述反射内涵,并解读反射三法则(http://blog.golang.org/laws-of-reflection)。 0 从类型和方法理解反射内涵 在基本的层面上,反射只是一个检查存储在接口变量中的类型和值的算法。使用反射机制,首先需要导入reflect包,reflect包中有两个重要类型需要了解,reflect.Type和reflect.Value,这两个类型使得可以访问变量的内容。与此相关的,还有两个简单的函数,reflect.TypeOf和reflect.ValueOf,可以从接口值中分别获取reflect.Type。 初学可能会认为reflect.Type是一种并列关系,但其实它们是一种包含关系,我们结合一段代码来理解这段话。 import( "fmt" "reflect" ) funcmain(){ varxfloat64=1.1 fmt.Println("reflect.Value:",reflect.ValueOf(x)) fmt.Println("reflect.Type:",reflect.TypeOf(x)) v:=reflect.ValueOf(x) fmt.Println("reflect.Type:",v.Type()) fmt.Println("actualvalue:",v.Float()) fmt.Println("kindisfloat64?",v.Kind()==reflect.Float64) } 其输出为: 根据程序及其结果,我们可以发现:在go语言中,每个值都包含两个内容:类型和实际的值。从类型角度来看,reflect.Value是一个关于<类型,实际的值>的二元组,而reflect.Type是值的类型,二者是包含关系。从方法角度来看,reflect.TypeOf 和 (reflect.ValueOf(x)).Type都可以返回reflect.Type;(reflect.ValueOf(x)).Float可以返回实际的值(类似的方法还包括(reflect.ValueOf(x)).Int、(reflect.ValueOf(x)).Bool等);(reflect.ValueOf(x)).Kind可以返回一个常量定义的类型。
根据上述分析,我们可以得出一个示意图,更为直观形象的表明值、类型、实际的值的关系。 此外,golang采用静态类型机制,TypeOf返回静态类型;但是,Kind返回底层类型。我们同样以一段代码来验证这段话。 import( "fmt" "reflect" ) typeMyIntint funcmain(){ varxMyInt=1 v:=reflect.ValueOf(x) fmt.Println("reflect.Type:",v.Type()) fmt.Println("kindisint?",v.Kind()==reflect.Int) } 输出: 1 法则一:从接口值到反射对象的反射(Reflection goes from interface value toreflection object) 前文所述内容其实就是从接口值到反射对象的反射,代表方法为reflect.ValueOf和reflect.TypeOf。可能有人会问,接口?接口在哪呢?我们来看一些前文提到这两个函数的声明,函数的参数是空接口,其实接口就在那里。关于golang的接口,大家可以参见我的另一篇博文《Golang中的接口》。 funcValueOf(iinterface{})Value funcTypeOf(iinterface{})Type 2 法则二:从反射对象到接口值的反射(Reflection goes from reflection object to interface value) 从reflect.Value可以使用Interface方法还原接口值;此方法可以高效地打包类型和值信息到接口表达中,并返回这个结果。方法声明: func(vValue)Interface()interface{} 通过反射对象 v 可以打印 float64 的表达值。 y:=v.Interface().(float64)//y将为类型float64。 fmt.Println(y) 还有更为简洁的实现。fmt.Println,fmt.Printf等其他所有传递一个空接口值作为参数的函数,在 fmt包内部解包的方式就像之前的例子这样。因此正确的打印reflect.Value的内容的方法就是将Interface方法的结果进行格式化打印(formatted print routine). fmt.Println(v.Interface()) 为什么不是fmt.Println(v)?因为v是一个 reflect.Value;这里希望获得的是它保存的实际的值。 我们修改前文代码还进行验证: funcmain(){ varxfloat64=1.1 fmt.Println("reflect.Value:",reflect.TypeOf(x)) v:=(reflect.ValueOf(x)) fmt.Println("reflect.Type:",v.Type()) fmt.Println("actualvalue(interface):",v.Interface()) fmt.Println("kindisfloat64?",serif;">其输出: |