Seeing as how there are no distinct Unmodifiable Collections interfaces, aren’t you just setting yourself up for runtime exceptions by returning Unmodifiable Collections from method invocations? example:
public class Start {
public static void main(String[] args) {
try {
List<String> listA = getListA();
listA.add("whatever");
System.out.println("listA: " + listA.get(0));
List<String> listB = getListB();
listB.add("whatever");
} catch(Exception e) {
System.out.println("SURPRISE!!! you should have known that listB was unmodifible.");
}
}
static List<String> getListA() {
return new ArrayList();
}
static List<String> getListB() {
return Collections.unmodifiableList(new ArrayList());
}
}
- At best, in javadoc I could write in bold letters “the returned Collection is Unmodifiable!”?
- In what scenario is returning Unmodifiable Collections worth the risk of runtime exceptions?
1
Returning Unmodifiable Collections only tees you up for runtime exceptions?
Yes and no.
-
Yes, it does do that.
-
No, it doesn’t only do that. It also prevents a class of problems from occurring; i.e. problems where some code has actually modified a collection that it wasn’t supposed to.
So what are the alternatives in Java?
-
Instead of returning an unmodifiable wrapper for a collection, you could return a copy of the original collection. Problems:
- Copying a large collection is expensive.
- The caller might end up unwittingly modifying the copy when it “needs” to modify the original. In other words, diagnosing the root (conceptual) problem becomes harder.
-
Try to express modifiable versus unmodifiable via classes and interfaces. People have tried … but it doesn’t work.
In short, the Java approach of unmodifiable collections that throw an exception when you try to modify them is probably the best solution possible in the context of the current Java type system. The exceptions are actually a good thing … because they make diagnosing the underlying problems easier.
At best, in javadoc I could write in bold letters “the returned Collection is Unmodifiable!”?
Yes. That is advisable. Though bold letters is probably inadvisable. (Too much shouting makes people deaf …)
In what scenario is returning Unmodifiable Collections worth the risk of runtime exceptions?
I would consider doing it whenever the correct behavior of the API depends on the caller not modifying the returned collection. (You do also need to weigh up the impact on performance; e.g. the cost of creating the wrapper objects, and the extra cost for the wrapped method calls.)
5
I don’t know why people think of such exceptions as risky. Developer writes code using add
. Developer thinks it might be a good idea to test the code he just wrote. Developer gets a big scary exception. Developer fixes code.
Much more risky is returning a mutable reference to a data structure which is still used internally to a class, perhaps to avoid an expensive defensive copy. Then that data structure is externally modified in a way the original class didn’t anticipate, causing a subtle bug that isn’t caught by testing, because it isn’t directly related to the code that was just changed.
Of course, this is far from the ideal way to handle immutable collections. Decent implementations have add
functions that return a new collection that shares as much as possible of the old collection without mutating it. However, as a quick wrapper to remove a certain set of errors, it’s not the worst thing.