Blog JVM Stuff.

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]
The decorate method returns a java.util.List descendant overwriting List#get(index). Whenever get(index) is called with an index greater than size, the list grows to the specified index and the factory creates a default value for this index. The gaps between the old and the new index will be filled with null. LazyList can be used in Grails command classes for dynamic lists.
class MyCommand {
    def dynamicProperty = LazyList.decorate([], { 1 } as Factory)
}
Whenever dynamicProperty[index] is accessed the list is grown to the specified index if needed and the provided factory creates a default value ("1" in the code listing above).

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]
As shown in the code listing, the gap is filled with null placeholders. There is another property of lazy lists that is worth noting at this point. Any subsequent calls to get(index) never return null, as null is only used as placeholder object. Thus, when calling get(index) referencing a slot with a placeholder, the default value will be returned. Or turning it around: this variant of lazy list never returns null when get(index) is called.
// ...
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 }
}
As this feature comes with Groovy 1.8.7 and 2.0, it will be available with one of the next Grails versions. Once bundled, commands objects can be defined without using Apache Commons, in a truly Groovy way :-)

[0] Grails Documentation - Command Objects
[1] Apache Commons LazyList