Groovy - 动态向此类的 metaClass 添加属性或方法

5

我希望能够动态地向当前对象的metaClass中添加字段和方法。我尝试了以下代码:

this.metaClass.testProp = "test"

要添加一个名为testProp的字段。然而,我却得到了

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

当我在类级别上进行相同操作时,例如将testProp添加到类而不是直接添加到对象

Process.metaClass.testProp = "test"

它能够正常工作,但是我的对象没有继承这个字段。不知道有什么好的建议或指导可以提供吗?非常感谢。

1个回答

7

简短回答:

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

长答案:

我认为,这里有三件事情让你感到困惑。第一件是全局注册表中存在元类。第二个是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。

将this.metaClass设置为null会将元类设置为Expando元类吗? - Dave Ankin
1
将其设置为null将把它设置为默认元类。如果你使用了 ExpandoMetaClass.enableGlobally() 或者它是Grails,那么它确实会是 ExpandoMetaClass - blackdrag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接