来自单一实例的元类(Per-Instance MetaClass)

为实例添加方法

通常当你要添加元方法(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”

8-O注意,你不能这么做:

gstr.metaClass = new ExpandoMetaClass( gstr.class )
gstr.metaClass.test = { println "test" }

因为你必须在实例的任何一个方法被调用之前调用emc.initialize()。但你不能在initialize()之后添加元方法!否则ExpandoMetaClass会拦截调用它自己的方法。解决办法就是(刚才第一个例子已经说明了)在给实例赋值一个新的元类之前简单地添加一个元方法。

另外还有一个方法,就是设置emc.allowChangesAfterInit = true,这将允许你在元类在使用时为之添加额外的方法。

8-O注意:

请确保使用合适的构造器——new ExpandoMetaClass(MyClass,false)。其中的false参数使得元类不被加入注册,否则你的新元类会被所有的MyClass的实例所应用,而不只是你指定的那个实例了。

:-o兼容性:

只能工作在Groovy 1.1-beta-3及以上版本中。你可以在更早的版本中使用代理类(Proxy class)来做到来自单一实例的对行为的改变。

如果你的实例不是一个Groovy对象

如果你的实例是一个普通的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)拦截了方法调用,而并非属性的访问。

 
wiki/user_guide/customizing_metaclass_for_a_single_instance.txt · 最后更改: 2008-06-27 14:12 由 johnny
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki