我们都知道,在Groovy语言中,我们可以使用MOP特性在运行期内动态添加属性或方法。
第一, 是给一个类添加属性或方法。也就是说,如果我们在运行期内给一个类添加了属性或方法,那么添加了以后,所有这个类实例化的对象,都将拥有了这个属性或方法。
第二, 第二,是给一个对象添加属性或方法。也就是说,如果我们在运行期内给一个对象添加了属性或方法,那么添加了以后,只有这个对象才拥有这个属性或方法。换句话说,如果我们再给这个对象的类实例化一个对象,那么该对象则不能拥有我们刚添加的那个属性或方法。
真的是这样吗?让我们举几个例子来看看吧。
使用ExpondoMetaClass在运行期内给一个类添加属性或方法是我们最最常用的一种在运行期内添加属性或方法的方法。比如,我们有如下的一个类:
class Testor1 {
}
现在,我们就尝试着使用ExpondoMetaClass在运行期内给Testor1添加一个方法,然后来测试它:
def t = new Testor1()
try
{
println 't invoke far'
t.far()
}
catch(Exception e)
{
}
Testor1.MetaClass."far" = {
->
println 'far'
}
def t1 = new Testor1()
println 't1 invoke far'
t1.far()
def t2 = new Testor1()
println 't2 invoke far'
t2.far()
测试很简单,我们在添加方法之前实例化一个Testor1对象,然后再添加方法之后实例化两个Testor1对象,分别来测试它们。
结果为:
t invoke far
t1 invoke far
far
t2 invoke far
far
从结果可以看出:在添加方法之前实例化的那个对象不能调用"far"方法,而添加方法之后实例化的两个对象都可以调用"far"方法。
其实,使用MOP特性在运行期内给类添加属性或方法还有一种方法来实现,它就是下面的这种方法:
def t = new Testor1()
try
{
println 't invoke far'
t.far()
}
catch(Exception e)
{
}
def mc = new ExpandoMetaClass(Testor1.class,true)
mc.far = {
->
println 'far'
}
mc.initialize()
def t1 = new Testor1()
println 't1 invoke far'
t1.far()
def t2 = new Testor1()
println 't2 invoke far'
t2.far()
}
就是使用ExpondoMetaClass类来实例化一个对象,如"def mc = new ExpandoMetaClass(Testor1.class,true)",再给该对象添加一个方法,如:
mc.far = {
->
println 'far'
}
然后,将这个对象实例化,如"mc.initialize()"。
上面代码的运行结果为:
t invoke far
t1 invoke far
far
t2 invoke far
far
跟一种方法的结果一样。
最后,我们来看看如何给一个对象在运行期内添加属性或方法,以及它们的运行结果。
def t = new Testor1()
try
{
println 't invoke far'
t.far()
}
catch(Exception e)
{
}
def t1 = new Testor1()
def emc = new ExpandoMetaClass( Testor1.class,false )
emc.far = { println "far" }
emc.initialize()
t1.MetaClass = emc
println 't1 invoke far'
t1.far()
def t2 = new Testor1()
try
{
println 't2 invoke far'
t2.far()
}
catch(Exception e)
{
}
我们还使用"def emc = new ExpandoMetaClass( Testor1.class,false )"语句来给一个对象添加属性或方法,但需要记住的是,第二个参数是"false"。接着,我们就可以添加方法了:
emc.far = { println "far" }
最后,还要做两件事,就是实例化该对象,并且把该对象赋值给t1对象的MetaClass属性。如:
emc.initialize()
t1.MetaClass = emc
上面代码的运行结果为:
t invoke far
t1 invoke far
far
t2 invoke far