通常当你要添加元方法(MetaMethod)时,它会被添加到目标类的所有对象实例。但是,对于Groovy对象来说,你可以通过为单独的对象实例提供元类(MetaClass)来动态地给该实例添加方法。
def test = "test" def gstr = "hello $test" // 这是实现了Groovy对象的GString def emc = new ExpandoMetaClass( gstr.class, false ) emc.test = { println "test" } emc.initialize() gstr.metaClass = emc gstr.test() // 打印“test”
注意,你不能这么做:
gstr.metaClass = new ExpandoMetaClass( gstr.class ) gstr.metaClass.test = { println "test" }
因为你必须在实例的任何一个方法被调用之前调用emc.initialize()。但你不能在initialize()之后添加元方法!否则ExpandoMetaClass会拦截调用它自己的方法。解决办法就是(刚才第一个例子已经说明了)在给实例赋值一个新的元类之前简单地添加一个元方法。
另外还有一个方法,就是设置emc.allowChangesAfterInit = true,这将允许你在元类在使用时为之添加额外的方法。
注意:
请确保使用合适的构造器——new ExpandoMetaClass(MyClass,false)。其中的false参数使得元类不被加入注册,否则你的新元类会被所有的MyClass的实例所应用,而不只是你指定的那个实例了。
兼容性:
只能工作在Groovy 1.1-beta-3及以上版本中。你可以在更早的版本中使用代理类(Proxy class)来做到来自单一实例的对行为的改变。
如果你的实例是一个普通的Java对象,它是无法当作Groovy对象来用的,是不具有元类属性的,所以你必须将你的实例包装在一个groovy.util.Proxy中:
ExpandoMetaClass emc = new ExpandoMetaClass( Object, false ) emc.boo = { "Surprise!" } emc.initialize() def obj = new groovy.util.Proxy().wrap( new Object() ) obj.setMetaClass( emc ) assert obj.boo() == "Surprise!"
注意这个例子里调用了setMetaClass(..)方法,而不是像前一个例子那样使用属性标记,这是因为代理(Proxy)拦截了方法调用,而并非属性的访问。