我想要覆盖metaClass上的动态属性,但它并不像我期望的那样工作。这里有一个简单的例子来演示这个问题。你可以在Groovy web console上在线运行它
class Bike {}
class Bell { def ring() { println("calling " + this) } }
def createNamespaces = {
return [ "bell": new Bell() ]
}
def resetNamespaces = { bike ->
// setting to null means 'set it to the default'
bike.metaClass = null
createNamespaces().each { name, namespace ->
println("setting " + namespace)
bike.metaClass."$name" = namespace
}
}
def bike= new Bike()
resetNamespaces(bike)
bike.bell.ring()
resetNamespaces(bike)
bike.bell.ring()结果是:
setting Bell@14e9bd2b
calling Bell@14e9bd2b
setting Bell@948a7ad
calling Bell@14e9bd2b因此,尽管我更改了metaClass上的属性,但调用它始终会返回设置的第一个对象。有没有某种缓存?
当我将示例的最后一部分更改为:
resetNamespaces(bike)
resetNamespaces(bike)
bike.bell.ring()那么结果就和预期的一样。也就是说,调用该属性将返回在metaClass上设置为最后一个的对象:
setting Bell@5b47e0c7
setting Bell@19f373a4
calling Bell@19f373a4我甚至尝试手动设置metaClass,如下所示
def resetNamespaces = { bike ->
def newMetaClass = new ExpandoMetaClass(Bike.class, true, true)
newMetaClass.initialize()
bike.metaClass = newMetaClass
...
}但结果仍然是一样的。所以一定涉及到一些缓存机制。我在文档中找不到有关此行为的任何内容。
发布于 2018-06-25 05:09:42
产生混淆的原因是您试图更改元类的属性,而不是方法。正如groovy metaprogramming官方文档中所述,属性是通过元类的setAttribute/getAttribute方法访问的。
// throws MissingFieldException!
def resetNamespaces = { bike ->
createNamespaces().each { name, namespace ->
bike.metaClass.setAttribute(bike, name, namespace)
}
}不幸的是,只有当属性在原始的类定义中时,这才能起作用,这里它抛出了一个MissingFieldException: No such 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()实际上,扩展也运行得很好。
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() https://stackoverflow.com/questions/50745518
复制相似问题