15

I often make a collection field unmodifiable before returning it from a getter method:

private List<X> _xs;
....
List<X> getXs(){
  return Collections.unmodifiableList(_xs);
}

But I can't think of a convenient way of doing that if the X above is itself a List:

private List<List<Y>> _yLists;
.....
List<List<Y>> getYLists() {
  return Collections.unmodifiableList(_yLists);
}

The problem in the above is of course that though the client cannot modify the List of lists, it can add/delete Y objects from the embedded lists.

Any thoughts?

Miserable Variable
  • 28,432
  • 15
  • 72
  • 133

6 Answers6

7

The best I could come up with uses ForwardingList from Google Collections. Comments are welcome.

private static <T> List<List<T>> unmodifiableList2(final List<List<T>> input) {
    return Collections.unmodifiableList(new ForwardingList<List<T>>() {
        @Override protected List<List<T>> delegate() {
            return Collections.unmodifiableList(input);
        }
        @Override public List<T> get(int index) {
            return Collections.unmodifiableList(delegate().get(index));
        }
    });
}
Miserable Variable
  • 28,432
  • 15
  • 72
  • 133
  • Especially after I made the outer list unmodifiable too :-) – Miserable Variable Jan 06 '09 at 10:39
  • 1
    What happens if it's a list of sets? – Craig P. Motlin Jan 06 '09 at 15:23
  • for a list of sets, you'd write similar code for the set? I.e., its still a 'hack' in the sense that this operation isnt natively supported by the language, the way a const is. – Chii Jan 07 '09 at 12:41
  • 1
    @Motlin: Yes, I actually did have a List> that I wanted to make const so yes you do need separate method. Worse, you can't name them all unmodifiableList because of erasure; so now I have unmodifiableListList and unmodifiableListMap. – Miserable Variable Jan 07 '09 at 14:38
  • @Chii: Well this is as much of a hack as COllections.unmodifableList is, no? I suspect we will see annotations @Unmodifiable, @Immutable (on objects), @NonModifying and @NonMutating (on methods) someday. – Miserable Variable Jan 07 '09 at 14:39
3

unfortunately, there is no easy way to get deep const-ness in java. you would have to hack around it by always making sure that the list inside the list is also unmodifiable.

i'd be interested too to know any elegant solution.

Chii
  • 14,540
  • 3
  • 37
  • 44
2

The clojure collections (map, set, list, vector) can all be nested and are immutable by default. For pure java, there is this library:

http://code.google.com/p/pcollections/

Arthur Edelstein
  • 1,567
  • 1
  • 10
  • 11
0

If you look at the implementation of the Collections.unmodifiable*(...) methods, you can see that they just wrap the collection. Doing a deep utility in same way should be doable.

The downside of this is that it adds extra method call to the collection access and so affects performance.

iny
  • 7,339
  • 3
  • 31
  • 36
0

If your only goal here is to enforce encapsulation, a classic solution is to use clone() or similar to return a structure that is not the internal state of the object. This obviously only works if all the objects can be cloned, and if the copied structure is small enough.

If this is a fairly commonly used data structure, another option is to make the API that accesses it more concrete, so that you have more detailed control over the specific calls. Writing your own List implementation, as above is one way to do this, but if you can narrow down the calls to specific use cases, you can expose specific access APIs instead of the List interface.

TREE
  • 1,292
  • 2
  • 12
  • 21
  • the problem with creating your own interfaces instead of using the standard java ones is that you cant take advantage of the many utility libraries that uses the java interfaces, unless you implemented them as well. IMHO, that is a price too high to pay for deep const-ness. – Chii Jan 07 '09 at 12:43
  • The stated collection (List of Lists) is not a standard collection. – TREE Jan 09 '09 at 19:36
0

Just in case someone is interested here is a simple solution:

    public List<List<Double>> toUnmodifiable(List<List<Double>> nestedList) {
        List<List<Double>> listWithUnmodifiableLists = new ArrayList<>();
            for (List<Double> list : nestedList) {              
                listWithUnmodifiableLists
                    .add(Collections.unmodifiableList(list));
            }
        return Collections.unmodifiableList(listWithUnmodifiableLists);
    }

This can be used for example as a solution if u want to expose a list with a getList() method, you can return: toUnmodifiable(mNestedList), where mNestedList is the private list in the class.

I personally found this useful when implementing a class used for parsing with GSON in Android, since it doesn't make sense to be able to modify a response, in this case the de-serialized json, I used this method as a way to expose the list with a getter and made sure the list wont be modified.

Ludvig W
  • 744
  • 8
  • 27