在Groovy中覆盖元类属性
我想覆盖元类上的动态属性,但它不能像我预期的那样工作。下面是一个简单的例子来说明这个问题。您可以在以下位置在线运行它: 结果是:在Groovy中覆盖元类属性,groovy,metaprogramming,Groovy,Metaprogramming,我想覆盖元类上的动态属性,但它不能像我预期的那样工作。下面是一个简单的例子来说明这个问题。您可以在以下位置在线运行它: 结果是: setting Bell@14e9bd2b calling Bell@14e9bd2b setting Bell@948a7ad calling Bell@14e9bd2b 因此,尽管我更改了元类的属性,调用它总是返回设置的第一个对象。是否存在某种缓存 当我将示例的最后一部分更改为: resetNamespaces(bike) resetNamespaces(bik
setting Bell@14e9bd2b
calling Bell@14e9bd2b
setting Bell@948a7ad
calling Bell@14e9bd2b
因此,尽管我更改了元类的属性,调用它总是返回设置的第一个对象。是否存在某种缓存
当我将示例的最后一部分更改为:
resetNamespaces(bike)
resetNamespaces(bike)
bike.bell.ring()
那么结果就如预期的那样了。也就是说,调用属性将返回在元类上设置为最后一个的对象:
setting Bell@5b47e0c7
setting Bell@19f373a4
calling Bell@19f373a4
我甚至尝试手动设置元类,如下所示
def resetNamespaces = { bike ->
def newMetaClass = new ExpandoMetaClass(Bike.class, true, true)
newMetaClass.initialize()
bike.metaClass = newMetaClass
...
}
但结果仍然是一样的。所以一定有一些缓存机制。我在文档中找不到有关此行为的任何信息。出现混淆的原因是您试图更改元类的属性,而不是方法。正如官方文档中所述,使用元类的setAttribute/getAttribute方法访问属性
// throws MissingFieldException!
def resetNamespaces = { bike ->
createNamespaces().each { name, namespace ->
bike.metaClass.setAttribute(bike, name, namespace)
}
}
不幸的是,这仅在属性位于原始类定义中时有效,在这里它抛出一个MissingFieldException:No-this-field:bell for class:Bike
另一方面,覆盖方法的工作方式很有魅力,通过添加动态getter方法提供所需的语法:
def resetNamespaces(bike) {
createNamespaces().each { name, namespace ->
bike.metaClass."get${name.capitalize()}" = { namespace }
}
}
def bike = new Bike()
resetNamespaces(bike)
bike.bell.ring()
resetNamespaces(bike)
bike.bell.ring()
实际上expando也可以很好地工作
class Bell {
def ring() { println this }
}
def bike = new Expando()
bike.createNamespaces = {
return [ bell : new Bell() ]
}
bike.resetNamespaces = {
bike.createNamespaces().each { name, namespace ->
bike."$name" = namespace
}
}
bike.resetNamespaces bike
bike.bell.ring()
bike.resetNamespaces bike
bike.bell.ring()
你说我不能改变对象实例元类的动态属性?它是否在文档或源代码中的某个地方,以便我可以找到它?我认为groovy对象上的每个方法/属性调用都被委托给它的元类。请参阅我问题中添加的最后一部分。对不起,您完全正确,当然您可以覆盖元类。。。请参阅我编辑的答案。无法对groovy内部提供更深入的见解,但我希望它能有所帮助。
class Bell {
def ring() { println this }
}
def bike = new Expando()
bike.createNamespaces = {
return [ bell : new Bell() ]
}
bike.resetNamespaces = {
bike.createNamespaces().each { name, namespace ->
bike."$name" = namespace
}
}
bike.resetNamespaces bike
bike.bell.ring()
bike.resetNamespaces bike
bike.bell.ring()