Reputation: 11585
Using Groovy 1.8. I'm trying to create a dynamic class definition that will cache properties per object. I did use propertyMissing
without adding the property to the object just fine. I just think caching the properties would be more efficient. Right?
Note that each instance must have its own different properties.
The code below works fine:
class C {}
def c = new C()
c.metaClass.prop = "a C property"
println c.prop
def x = new C()
x.prop
will output:
a C property
groovy.lang.MissingPropertyException: No such property: prop for class: C
If I need to this problematically:
class A {
def propertyMissing(String name) {
if(!this.hasProperty(name)) {
println "create new propery $name"
this.metaClass."$name" = "Dyna prop $name"
println "created new propery $name"
}
this.metaClass."$name"
}
}
a = new A()
println a.p1
For A
, I get as far as "create new property", but the line this.metaClass."$name" = "Dyna prop $name"
fails with: No such property: p1 for class at line 5
Whats wrong?
Upvotes: 5
Views: 16755
Reputation: 2666
Why don't you store your dynamic properties in a simple HashMap?
class Foo {
def storage = [:]
def propertyMissing(String name, value) { storage[name] = value }
def propertyMissing(String name) { storage[name] }
}
def f = new Foo()
f.foo = "bar"
This is the standard example from: http://groovy.codehaus.org/Using+methodMissing+and+propertyMissing
Note, you don't have to check if the property is really missing ... if it won't be missing, the method would not be called in the first place.
Upvotes: 6
Reputation: 171054
This code should do what you want:
class A {
A() {
def mc = new ExpandoMetaClass( A, false, true)
mc.initialize()
this.metaClass = mc
}
def propertyMissing( String name ) {
println "create new propery $name"
def result = "Dyna prop $name"
this.metaClass."$name" = result
println "created new propery $name"
result
}
}
a = new A()
println a.p1
println a.p1
That outputs:
create new propery p1
created new propery p1
Dyna prop p1
Dyna prop p1
Upvotes: 8
Reputation: 6287
ExpandoMetaClass is not really designed to be used after initialize or instance variables (see reference below at least for 1.6 or older).
Maybe you want to use Runtime mixins. More information here and here.
You may want to read a blogpost, which a workaround is provided to define properties at runtime. And the related groovy bug (that is not really a bug) related to it.
I saw in the comments section and verified at least in version 1.6 ExpandoMetaClass documentation:
By default methods are only allowed to be added before initialize() is called. In other words you create a new ExpandoMetaClass, add some methods and then call initialize(). If you attempt to add new methods after initialize() has been called an error will be thrown.
Upvotes: 1