本篇介绍groovy 的MetaClasses ,MetaClass 是Groovy 动态编程比较重要的组成。
(以下出现的代码片段是在groovy1.5.3测试通过,希望各位可以运行之)。
1.MetaClass
groovy中出现的每个类都有其对应的MetaClass。MetaClas是一个接口,该接口扩展了MetaObjectProtocol接口,这就是我们通常所说的MOP协议了。
MetaClass的实现类主要有
MetaClassImpl 是通用的实现类。ClosureMetaClass是闭包类特有的MetaClass。ExpandoMetaClass是用户扩展groovy类行为时用到的MetaClass
println 'first:---> '+'test'.metaClass
println 'String.metaClass:---> '+String.metaClass
println 'after String.metaClass:---> '+'test'.metaClass
String.metaClass:---> groovy.lang.ExpandoMetaClass@8a0d5d[class java.lang.String]
after String.metaClass:---> groovy.lang.ExpandoMetaClass@8a0d5d[class java.lang.String]
对于这样的结果你是不是会感觉到奇怪呢?
下面我将结合Groovy实现代码来解释这个过程
第一句脚本 通过String实例对象来获得,Groovy为其初始化的groovy.lang.MetaClassImpl@183f74d[class java.lang.String]对象。这个对象将在MetaClassRegistryImpl中和String类关联起来。也就是注册了String.class和
groovy.lang.MetaClassImpl的对象关系。
第二句脚本 通过String类去访问 metaClass对象,不过这一次是调用了类DefaultGroovyMethods中的方法
/**
* Adds a "metaClass" property to all class objects so you can use the syntax
* <code>String.metaClass.myMethod = { println "foo" }</code>
*
* @param c The java.lang.Class instance
* @return An MetaClass instance
*/
public static MetaClass getMetaClass(Class c) {
MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
MetaClass mc = metaClassRegistry.getMetaClass(c);
if (mc instanceof ExpandoMetaClass
|| mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass)
return mc;
else {
MetaClass emc = ExpandoMetaClassCreationHandle.instance.create(c, metaClassRegistry);
emc.initialize();
metaClassRegistry.setMetaClass(c, emc);//并且在metaClassRegistry中注册
return emc;
}
}
第三句脚本又重新通过String实例对象来获得了,但这一次得到的是groovy.lang.ExpandoMetaClass@8a0d5d,这是因为第二句话中对String和其MetaClass对象进行了重新注册。
自此以后系统中所有的String对象的MetaClass对象都一直市是ExpandoMetaClass对象实例。
由于我们举的String例子有点特殊
我们看看对于自定义类的例子
class Foo{
}
def f1 = new Foo();
println f1.metaClass
println Foo.metaClass
println f1.metaClass
def f2 = new Foo()
println f2.metaClass
结果是不是又不一样了?
f1对象的metaClass是被缓存了,只有在调用过
Foo.metaClass
之后,new出来的对象才会使用ExpandoMetaClass