我想在功能上实现,允许在尝试设置它的同时向类添加未知属性,而不使用map
动态属性。
由于Groovy允许使用metaClass
执行此操作,因此我在propertyMissing
方法中使用了它。
class Item {
def propertyMissing(String name, value) {
this.class.metaClass."$name" = value
}
}
但我遇到了一种奇怪的行为。
def i1 = new Item()
i1.prop = "value"
println i1.properties // [class:class Item]
println i1.prop // null
i1.metaClass.field = "555"
println i1.properties // [prop:null, class:class Item, field:555]
println i1.prop // null
i1.prop = "value1"
println i1.properties // [prop:value1, class:class Item, field:555]
println i1.prop // value1
此外,如果我在尝试在示例中设置metaClass
之前访问prop
,则不再添加
def i1 = new Item()
i1.metaClass.unkn = "1111"
i1.prop = "value"
println i1.properties // [class:class Item, unkn:1111]
println i1.prop // null
i1.metaClass.field = "555"
println i1.properties // [class:class Item, unkn:1111, field:555]
println i1.prop // null
i1.prop = "value1"
println i1.properties // [class:class Item, unkn:1111, field:555]
println i1.prop // null
为什么会出现这种行为?
答案 0 :(得分:2)
当您动态更新对象的元类时,Groovy会使用ExpandoMetaClass替换元类。它是MetaClass的一个特殊实现,支持添加和删除属性/方法。
但是,在您的示例中,Item
是GroovyObject
,它在MetaClass上有一个持久字段。交换MetaClass时不更新此字段:只有注册表中的元类被ExpandoMetaClass替换。这种代码可以使用javaobject,因为这个对象没有字段,每次groovy访问元类时都会执行分辨率class->元类。
你知道要在groovy对象上添加属性,你应该显式设置一个ExpandoMetaClass:
class Item {
def Item() {
def mc = new ExpandoMetaClass(Item, false, true)
mc.initialize()
this.metaClass = mc
}
def propertyMissing(String name, value) {
this.metaClass."$name" = value
}
}
答案 1 :(得分:1)
您遇到的一个问题是,您尝试将属性添加到类MetaClass
而不是实例MetaClass
。并且因为您在创建实例后添加了属性,所以实例看不到它。例如,此代码无法打印属性:
class A { }
def a = new A()
A.metaClass.prop = 'value'
println a.prop
错误很有趣:groovy.lang.MissingPropertyException: No such property: prop for class: A
Possible solutions: prop
但是,即使您更改代码以使用实例MetaClass
,它仍然无效:
class Item {
def propertyMissing(String name, value) {
metaClass."$name" = value
}
}
def i1 = new Item()
i1.prop = 'value'
assert i1.prop == 'value'
错误提供了线索:
groovy.lang.MissingPropertyException: No such property: prop for class: groovy.lang.MetaClassImpl
提供类似MetaClass
功能的Map
是ExpandoMetaClass
。在您执行以下操作之前,对象通常不会获得此类MetaClass
:
instance.metaClass.prop = 'value'
因此MetaClass
不是ExpandoMetaClass
的事实意味着没有发生替换过程。在MOP过程中,propertyMissing()
被调用的可能性太晚,无法以这种方式使用MetaClass
。
您提到要使用Map
动态属性添加不带的属性。但是,ExpandoMetaClass
,这是您尝试间接使用的,使用...
... Map
的动态属性!你可以看到它here。
实现您正在寻找的行为的最简单方法是扩展 Expando
:
class Item extends Expando {
def anotherProperty = 'Hello'
}
def i1 = new Item()
i1.prop = 'value'
assert i1.prop == 'value'
assert i1.anotherProperty == 'Hello'
Expando
为您完成所有工作。如果您想了解其工作原理,请阅读this。