Written on
Groovy 2.0: Love for Grails Command Objects
Grails supports command object binding [0]. Request parameters are bound to properties of a given command class. From this context, a special-case is the use of lazy list properties. A lazy list grows whenever an index greater than its size is requested. Apache Commons provides a LazyList decorator implementation [1].
@Grab('commons-collections:commons-collections:3.2.1')
import org.apache.commons.collections.Factory
import org.apache.commons.collections.list.LazyList
def lazy = LazyList.decorate([], { 1 } as Factory)
assert lazy.get(4) == 1
assert lazy.get(1) == 1
assert lazy == [null, 1, null, null, 1]
class MyCommand {
def dynamicProperty = LazyList.decorate([], { 1 } as Factory)
}
Doin' it the Groovy way
Groovy 2.0 comes with a new GDK method emulating Commons LazyList behavior: List.withDefault. Whenever a list is created by calling withDefault it returns a lazy list with exactly the same properties as described above:
def l = [].withDefault { 42 }
l[3] == 42 // the index has not been available
assert l == [null, null, null, 42]
// ...
assert l == [null, null, null, 42]
assert l[0] == 42
assert l[1] == 42
assert l[2] == 42
Lazy and Eager Lists
So far only half the truth has been told. Internally, withDefault redirects to withLazyDefault, as there is another lazy list variant available: withEagerDefault. This method can be used to create an eager instead of a lazy list. Again, eager lists grow as indices greater than the actual size are accessed, but it does fill gaps with default values instead of null placeholders. As a consequence, eager lists might hold null values as valid list elements too.
def l = [].withEager { 42 }
assert l[3] == 42
assert l == [42, 42, 42, 42]
l = [null].withEager { 42 }
assert l[0] == null
Conclusion
class MyCommand {
def dynamicProperty = [].withDefault { 1 }
}