Grails supports command object binding [0]. Request parameters are bound to properties of a given command class. From this(binding [0]) 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 method.

A lazy list grows whenever an index greater than its size is requested


@Grab('commons-collections:commons-collections:3.2.1')
import org.apache.commons.collections.Factory
import org.apache.commons.collections.list.LazyList

def lazyList = LazyList.decorate([], { 1 } as Factory)
assert lazyList.get(3) == 1
assert lazyList.get(1) == 1
assert lazyList == [null, 1, 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 MyCommandObj {
    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).

Doing it in 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 lazyList = [].withDefault { 11 }
lazyList[3] == 11 // the index has not been available
assert lazyList == [null, null, null, 11]

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 lazyList == [null, null, null, 11]

assert lazyList[0] == 11
assert lazyList[1] == 11
assert lazyList[2] == 11

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 lazyList = [].withEager { 42 }

assert lazyList[3] == 42
assert lazyList == [42, 42, 42, 42]

lazyList = [null].withEager { 42 }

assert lazyList[0] == null

Conclusion

 
class MyCommandObj {
    def dynamicProperty = [].withDefault { 1 }
}

Hope this will help you in understanding the lazy list with command object 🙂