Details
-
Type:
Bug
-
Status:
Closed
-
Priority:
Minor
-
Resolution: Fixed
-
Affects Version/s: 1.8.1
-
Fix Version/s: 2.0-beta-3, 1.8.7
-
Component/s: groovy-jdk
-
Labels:
-
Number of attachments :
Description
I'm not sure if this should even be logged as a bug, but here goes:
I was playing around with the Apache Commons ListUtils.lazyList. This list will automatically create an item if an index is not yet defined (or is null). Example code:
@Grab(group='commons-collections', module='commons-collections', version='[1.3,)') import org.apache.commons.collections.ListUtils import org.apache.commons.collections.Factory import groovy.transform.Canonical @Canonical class Test { String name int amount } List<Test> t = ListUtils.lazyList([], { new Test() } as Factory) // UNCOMMENT to make the example work: // t.get(1) // Thows NPE here: t[1].name = 'Jim' t[0].amount = 6 assert t == [new Test(null, 6), new Test('Jim', 0)]
However, I thought it was broken, because I had been running with without the commented t.get(1) line. The getAt code checks the size of the dynamic list, and since it is smaller, returns null. This causes an NPE to be thrown.
I realize that the lazy list breaks the List contract (by dynamically changing the list size). However, it seems like a fairly useful feature to have a lazily created list.
I'm not sure there is an acceptable solution, but returning null hides the problem in a way that is hard to debug! Maybe it would be better to have a withDefault method for lists, too, that works like the one for {{Map}}s. That would provide a usable solution without breaking the current design. Plus, you no longer have to include the commons library for that use case. ![]()
Yes, I think it would be good to introduce a ListWithDefault in Groovy similar to MapWithDefault. We could mimic the behaviour in the commons collection LazyList as you suggest. Here is the javadoc for anyone interested:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/list/LazyList.html
We would also need to add a DGM#withDefault(List, Closure) method.
A variation we could potentially support is a boolean flag to indicate eager creation of intervening index entries, i.e. DGM#withDefault(List, Closure, Boolean) with default to false, i.e. we would mimic LazyList and not support null as a valid list value. With the boolean set to true, we could support nulls but if you had a list with 2 elements (index values 0 and 1), then calling get at index 4 would cause the closure to be called for index values 2, 3 and 4 - rather than calling it just for 4 and setting the others to null.