Groovy - 动态添加属性或方法到metaClass

时间:2015-09-05 10:16:31

标签: groovy

我想动态地将字段和方法添加到当前对象的metaClass。我试过了

this.metaClass.testProp = "test"

添加名为testProp的字段。但是,我得到了

groovy.lang.MissingPropertyException: No such property: testProp for class: groovy.lang.MetaClassImpl

当我在课堂上做同样的事情,例如将testProp添加到类中而不是直接添加到对象

Process.metaClass.testProp = "test"

它有效,但我的对象继承该字段。任何有正确方向的想法或指示都将受到高度赞赏。

1 个答案:

答案 0 :(得分:6)

简答:

Process.metaClass.testProp = "test"
this.metaClass = null
assert this.testProp == "test"

答案很长:

我认为,有三件事让你在这里遇到问题。第一个是meta类的全局注册表。第二个是Groovy允许每个实例元类(这是Groovy类的默认值)。第三个是默认元类不允许运行时元编程。

那么如果你进行运行时元编程会发生什么,默认需要由ExpandoMetaClass(EMC)取代,以实现这一点。但由于每个实例都有元类逻辑,因此这种替换可能不会影响所有实例。实例的元类在第一次使用时从全局注册表中获取。 Process.metaClass.testProp = "test"更改全局保存的一个。任何已经有元类的实例都不知道更改,因此不知道属性。 this.metaClass.testProp = "test"将仅更改当前实例的元类,其他实例可能不知道它。您可以使用this.metaclass = null重置它。它将再次从全局注册表中请求元类。如果您对每个实例进行了更改,那么您的更改就会消失。如果您进行了全局更改,则现在可以看到您的更改。

如果你使用Groovy中的默认元类(MetaClassImpl),这一切都很重要。如果您在代码中进行类似更改,则新的元类将是ExpandoMetaClass(EMC)。此元类允许更改,因此进一步更改不会导致替换元类。要确保所有实例从一开始就使用ExpandoMetaClass,您通常会使用以下类似的设置代码:ExpandoMetaClass.enableGlobally()

所以要解决你的问题我可以简单地做到这一点

Process.metaClass.testProp = "test"
this.metaClass = null
assert this.testProp == "test"

如果您之前在某些设置代码中使用了ExpandoMetaClass.enableGlobally(),则可以将元类的重置代码保留(或者如果全局元类与“this”的类相同,如果它是EMC )。例如,Grails使用EMC作为默认值