0

The current java framework provides a Collection class and then a way to get an "unmodifiable" collection from a collection. There is no class corresponding to an unmodifiable collection that is publicly exposed. If I were to re-design a Collection class hierarchy I would have a Collection interface (which is read only) with a subclass of ModifiableCollection (which is can be modified). The current approach is inadequate because it only provides run time error checking as opposed to compile time checking. With an explicit read only collection class at the base of the hierarchy run-time errors would be avoided.

Note that I am referring to Read Only collections here, not immutable, although the concepts are similar. See https://stackoverflow.com/a/27611460/4350148.

The question was motivated partly due to issues I was encountering in writing code. I found that sometimes I was calling Collections.unmodifiableCollection(Collection col) on collections that are already unmodifiable. The method simply wraps the collection again. With a read only version it would be more clear for every method, what kind of collection is returned and also whether a method can change the contents of one of it's collection type parameters.

Would this approach make more sense? Or am I missing something here?

Community
  • 1
  • 1
dan b
  • 1,172
  • 8
  • 20
  • If you pass a mutable collection to a method which assumes it's immutable, but you change this collection elsewhere in code, possibly in another thread. Then its not really immutable, just that the callee can't change it, unless the method with the immutable reference casts it to a mutable one ... – Peter Lawrey Dec 22 '14 at 12:18
  • 1
    @PeterLawrey I used read only here, I was not referring to immutable. They are not the same – dan b Dec 22 '14 at 13:31

2 Answers2

3

This was considered and rejected, because it would have led to too many interfaces and classes in the Collections hierarchy :

Why don't you support immutability directly in the core collection interfaces so that you can do away with optional operations (and UnsupportedOperationException)?

This is the most controversial design decision in the whole API. Clearly, static (compile time) type checking is highly desirable, and is the norm in Java. We would have supported it if we believed it were feasible. Unfortunately, attempts to achieve this goal cause an explosion in the size of the interface hierarchy, and do not succeed in eliminating the need for runtime exceptions (though they reduce it substantially).

Doug Lea, who wrote a popular Java collections package that did reflect mutability distinctions in its interface hierarchy, no longer believes it is a viable approach, based on user experience with his collections package. In his words (from personal correspondence) "Much as it pains me to say it, strong static typing does not work for collection interfaces in Java."

To illustrate the problem in gory detail, suppose you want to add the notion of modifiability to the Hierarchy. You need four new interfaces: ModifiableCollection, ModifiableSet, ModifiableList, and ModifiableMap. What was previously a simple hierarchy is now a messy heterarchy. Also, you need a new Iterator interface for use with unmodifiable Collections, that does not contain the remove operation. Now can you do away with UnsupportedOperationException? Unfortunately not.

Consider arrays. They implement most of the List operations, but not remove and add. They are "fixed-size" Lists. If you want to capture this notion in the hierarchy, you have to add two new interfaces: VariableSizeList and VariableSizeMap. You don't have to add VariableSizeCollection and VariableSizeSet, because they'd be identical to ModifiableCollection and ModifiableSet, but you might choose to add them anyway for consistency's sake. Also, you need a new variety of ListIterator that doesn't support the add and remove operations, to go along with unmodifiable List. Now we're up to ten or twelve interfaces, plus two new Iterator interfaces, instead of our original four. Are we done? No.

Consider logs (such as error logs, audit logs and journals for recoverable data objects). They are natural append-only sequences, that support all of the List operations except for remove and set (replace). They require a new core interface, and a new iterator.

And what about immutable Collections, as opposed to unmodifiable ones? (i.e., Collections that cannot be changed by the client AND will never change for any other reason). Many argue that this is the most important distinction of all, because it allows multiple threads to access a collection concurrently without the need for synchronization. Adding this support to the type hierarchy requires four more interfaces.

Now we're up to twenty or so interfaces and five iterators, and it's almost certain that there are still collections arising in practice that don't fit cleanly into any of the interfaces. For example, the collection-views returned by Map are natural delete-only collections. Also, there are collections that will reject certain elements on the basis of their value, so we still haven't done away with runtime exceptions.

When all was said and done, we felt that it was a sound engineering compromise to sidestep the whole issue by providing a very small set of core interfaces that can throw a runtime exception.

(Source)

Eran
  • 387,369
  • 54
  • 702
  • 768
  • Thanks for posting this. I don't agree with "What was previously a simple hierarchy is now a messy hierarchy." It's true that the number of collection classes would double, but conceptually it doesn't add much complexity. It's funny that adding those was considered to be a "messy hierachy". I also don't understand the statement about arrays and the need for variable size list class. Your thoughts – dan b Dec 22 '14 at 12:38
  • @danb Consider `Arrays.asList()`. This method returns a `List` implementation that throws an exception if you try to call `add` or `remove`. If you wanted to eliminate all the `UnsupportedOperationException` exception, you'd need some `FixedLengthList` interface for converting an array to a List. It's just one more example of all the interfaces they would have to add to eliminate the `UnsupportedOperationException` exception. Having too many interfaces would make it that much harder to learn how to use the Collections hierarchy. – Eran Dec 22 '14 at 12:44
  • I think you would just need a List (read only), and a ModifiableList (as already discussed) and two methods Array.asList() -> returns List and Arrays.asModifiableList()-> return ModifiableList. So it's just another mirrored method. – dan b Dec 22 '14 at 13:29
  • @danb But the List returned by Arrays.asList is not immutable. You can't add or remove elements, but you can set the i'th element. It behaves like arrays. That's why it would require an additional interface. – Eran Dec 22 '14 at 13:32
  • Yes but I am only referring to Read Only here, not Immutable, which is a different interface. So if I did `List` l = Arrays.asList(1,2)` I could not change it by l.set(0,2)) since that method is not available in the List interface. – dan b Dec 22 '14 at 13:35
0

Collection inherits from Iterable which is a read-only version. Iterable can simply be used to iterate through the Collection and is sort of read-only version of any Collection.

For other operations on an Iterable you might convert it to a Stream which is also read-only using e.g. StreamSupport.

See this SO for more input.

Community
  • 1
  • 1
wassgren
  • 18,651
  • 6
  • 63
  • 77