覆盖已通过expandoMetaClass添加的方法

时间:2016-10-07 13:47:15

标签: groovy metaprogramming

我无法理解GroovyClassLoader缓存如何与expando方法一起使用。

特别是,我希望下面的代码为每次调用Number.test()提供不同的输出,但事实并非如此。

int i = 1

Number.metaClass.test = {
  return "A string"
}
println i.test()

println GroovySystem.metaClassRegistry.getMetaClass(Number.class)
Number.metaClass = null
println GroovySystem.metaClassRegistry.getMetaClass(Number.class)

Number.metaClass.test = {
  return "Another String"
}
println i.test()

println GroovySystem.metaClassRegistry.getMetaClass(Number.class)
GroovySystem.metaClassRegistry.removeMetaClass(Number.class)
println GroovySystem.metaClassRegistry.getMetaClass(Number.class)

Number.metaClass.test = {
  return "Yet another String"
}
println i.test()

输出:

A string
groovy.lang.ExpandoMetaClass@3571b748[class java.lang.Number]
groovy.lang.MetaClassImpl@7748410a[class java.lang.Number]
A string
groovy.lang.ExpandoMetaClass@37f1104d[class java.lang.Number]
groovy.lang.MetaClassImpl@55740540[class java.lang.Number]
A string

修改

我尝试了更多的东西。看起来我的系统表现得非常奇怪。给出以下代码:

def i = 1

i.class

Number.metaClass.test = {
  return "A string"
}
println i.test()

Number.metaClass = null
Number.metaClass.test = {
  return "Another String"
}
println i.test()

GroovySystem.metaClassRegistry.removeMetaClass(Number.class)
Number.metaClass.test = {
  return "Yet another String"
}
println i.test()

当我在我的系统上执行它时,它按预期工作(在输出中获得3个不同的字符串)但如果我评论所谓无用的A string,则它会失败(3 x i.class)。

但是,如果我在groovy-console.appspot.com上执行它,它会给我带有或不带该行的预期输出......

我有点不知所措。

1 个答案:

答案 0 :(得分:1)

创建int i = 9时,它创建了一个包含当前Number.metaClass的对象。它接受创建一个新方法(读Number.metaClass.test = { "A string" }),但不允许重载已存在的方法。您可以改为改变对象的元类:

int i = 1

Number.metaClass.test = { "A string" }
assert i.test() == "A string"

Number.metaClass = null

i.metaClass.test = { "Another String" }
assert i.test() == "Another String"

GroovySystem.metaClassRegistry.removeMetaClass(Number.class)
assert i.test() == "Another String"

i.metaClass.test = { "Yet another String" }
assert i.test() == "Yet another String"

需要调用i.class对我来说看起来像个错误...看看jira我看到了一些未解决的问题和打开的错误。我认为你的看起来与这些相似:

  

在更改实例metaClass之前,Java类metaClass上的覆盖方法不会生效:https://issues.apache.org/jira/browse/GROOVY-5065

     

使用metaClass覆盖类层次结构中的方法不能按预期工作:https://issues.apache.org/jira/browse/GROOVY-3942

     

通过.metaClass重写方法的行为不一致:https://issues.apache.org/jira/browse/GROOVY-6847

     

删除metaClass方法:https://issues.apache.org/jira/browse/GROOVY-4189

如果你考虑一个具体的课程,我的答案的第一部分是有效的:

class Echo {}
Echo.metaClass.test = { "A string" }
def i = new Echo()
assert i.test() == "A string"

Echo.metaClass = null
Echo.metaClass.test = { "Another String" }
assert i.test() == "A string"

i.metaClass.test = { "Another String" }
assert i.test() == "Another String"

i.metaClass.test = { "Yet another String" }
assert i.test() == "Yet another String"