itations of the language and have to decide between our deep-seated sense of what is right and
our engineering desire to do something that will work and can be understood.
Rather than try to reflect this variation in the type system, the original designers of the col-
lections library decided that it would be better to introduce an exception that could be thrown
by classes that are specialized in such a way that they don't implement all of the methods in
their interface. So a read-only Set , on this theory, would still have an add() method. But the
implementation of that method would throw an UnsupportedOperationException . Further,
since programmers wouldn't want to have to declare this exception on every method that in
the future might not be implemented in a collection class, and since programmers using the
collections wouldn't want to be forced to guard against that exception (especially for those
implementations that would never throw the exception, because they do implement all of the
interface), the exception was made a runtime exception. It needs to be neither declared as part
of a method signature nor caught by the caller of the method.
Which, of course, calls into question the notion of Java as a type-safe language. If you squint
really hard, perhaps it is true that a read-only Set implements the add operation, but that im-
plementation is done in a way that causes your program to fail with an uncaught exception.
Implementing methods by throwing this exception is, fortunately, not very common. In fact,
the main use these days is in interactive development tools, which use this exception as a de-
fault implementation of methods in automatically generated code. But you should check the
documentation for collections carefully to see whether the collection throws this exception for
any part of the implementation. If they use it for an implementation without documentation,
you should get a better collection implementation.
I will end this chapter with a simple observation. The collections in Java are useful additions
to every programmer's toolkit. But they are also the reason for introducing generics (in the
name of type safety) and the UnsupportedOperationException (in the name of simplicity).
As long as neither one bites you, the irony is rather entertaining. It is also an indication that the
Java environment is a complex creation resulting from the efforts of many people over a reas-
onably long period of time. When the UnsupportedOperationException was introduced,
one set of designers made the decision that a flatter type hierarchy was preferable to strong
typing. When generics and parameterized types were introduced (some number of years later),
a (mostly) different set of designers made the decision that compiler type checking was vital.
Being separated by time, the two groups couldn't have gotten together to make their decisions
consistent. As we have seen before, history clarifies stupidity.