我想覆盖元类上的动态属性,但它没有按我的预期工作。这是一个演示该问题的简单示例。您可以在线运行它 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
因此,尽管我更改了元类的属性,但调用它始终返回设置的第一个对象。有某种缓存吗?
当我将示例的最后一部分更改为:
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
...
}
但是结果还是一样。所以必须涉及一些缓存机制。我在文档中找不到有关此行为的任何信息。
最佳答案
之所以会出现困惑,是因为您试图更改元类的属性,而不是方法。正如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()
实际上,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 中覆盖元类属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50745518/