views:

1317

answers:

4

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?

+3  A: 

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
And me, as well!
Software Monkey
A: 

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
+2  A: 

The best I could come up with uses http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/package-summary.html">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));
        }
    });
}
Hemal Pandya
Thats pretty much fine.
Adeel Ansari
Especially after I made the outer list unmodifiable too :-)
Hemal Pandya
What happens if it's a list of sets?
Craig P. Motlin
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
@Motlin: Yes, I actually did have a List<Map<K,V>> 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.
Hemal Pandya
@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.
Hemal Pandya
A: 

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
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
The stated collection (List of Lists) is not a standard collection.
TREE