12

Looking through the Java Collections Framework, I've noticed quite a few of the interfaces have the comment (optional operation). These methods allow implementing classes to through an UnsupportedOperationException if they just don't want to implement that method.

An example of this is the addAll method in the Set Interface.

Now, as stated in this series of questions, interfaces are a defining contract for what the use can expect.

Interfaces are important because they separate what a class does from how it does it. The contract defining what a client can expect leaves the developer free to implement it any way they choose, as long as they uphold the contract.

and

An interface is a description of the actions that an object can do... for example when you flip a light switch, the light goes on, you don't care how, just that it does. In Object Oriented Programming, an Interface is a description of all functions that an object must have in order to be an "X".

and

I think the interface-based approach is significantly nicer. You can then mock out your dependencies nicely, and everything is basically less tightly coupled.

What is the point of an interface?

What are interfaces?

Interface + Extension (mixin) vs Base Class

Given that the purpose of interfaces is to define a contract and make your dependencies loosely coupled, doesn't having some methods throw an UnsupportedOperationException kind of defeat the purpose? It means I can no longer be passed a Set and just use addAll. Rather, I have to know what implementation of Set I was passed, so I can know if I can use addAll or not. That seems pretty worthless to me.

So what's the point of UnsupportedOperationException? Is it just making up for legacy code, and they need to clean up their interfaces? Or does it have a more sensical purpose that I'm missing?

  • I do not know which JRE you are using, but my Oracle version 8 does not define addAll in HashSet. It defers to the default implementation in AbstractCollection which most certainly does not throw UnsupportedOperationException. – user22815 Jul 10 '15 at 17:48
  • @Snowman You're right. I missread the docs. I will edit my question. – MirroredFate Jul 10 '15 at 17:51
  • 1
    I like to start up Eclipse and look at the source code, bouncing around code references and definitions to make sure I have it right. As long as the JRE is linked to src.zip it works great. It helps to know exactly what code the JRE is running sometimes and not defer to the JavaDoc which can be a bit verbose. – user22815 Jul 10 '15 at 17:54
12

Look at the following interfaces:

These interfaces all declare mutating methods as optional. This is implicitly documenting the fact that the Collections class is able to return implementations of those interfaces that are immutable: that is, those optional mutation operations are guaranteed to fail. However, per the contract in the JavaDoc, all implementations of those interfaces must allow the read operations. This includes the "normal" implementations such as HashSet and LinkedList as well as the immutable wrappers in Collections.

Contrast with the queue interfaces:

These interface do not specify any optional operations: a queue, by definition, is designed to offer and poll elements in a FIFO manner. An immutable queue is about as useful as a car without wheels.


One common idea that comes up repeatedly is having an inheritance hierarchy that has both mutable and immutable objects. However, these all have drawbacks. The complexity muddies the waters without actually solving the problem.

  • A hypothetical Set could have the read operations, and a subinterface MutableSet could have the write operations. Liskov tells us that a MutableSet could then be passed in to anything that needs a Set. At first this sounds okay, but consider a method that expects the underlying set will not be modified while read: it would be possible for two threads to use the same set and violate the invariant of the set not changing. This could cause a problem e.g. if a method reads an element from the set twice and it is there the first time but not the second time.

  • Set could have no direct implementations, instead having MutableSet and ImmutableSet as subinterfaces which are then used for implementing classes. This has the same issue as above: at some point in the hierarchy, an interface has conflicting invariants. One says "this set must be mutable" and the other says "this set cannot change."

  • There could be two completely separate hierarchies for mutable and immutable data structures. This adds a ton of extra complexity for what ends up being very little gain. This also has the specific weakness of methods that do not care about mutability (e.g. I just want to iterate a list) must now support two separate interfaces. Since Java is statically typed, this means extra methods to handle both interface hierarchies.

  • We could have a single interface and allow implementations to throw exceptions if a method is not applicable to it. This is the route Java took, and it makes the most sense. The number of interfaces is kept to a minimum, and there are no mutability invariants because the documented interface makes no guarantees about mutability either way. If an immutability invariant is required, use the wrappers in Collections. If a method does not need to change a collection, simply do not change it. The drawback is a method cannot guarantee a collection will not change in another thread if it is provided a collection from outside, but that is a concern of the calling method (or its calling method) anyway.


Related reading: Why doesn't Java 8 include immutable collections?

  • 1
    But if methods are optional, what place do they have in the interface? Shouldn't there be a separate interface containing the optional methods, for example MutableCollection? – MirroredFate Jul 10 '15 at 18:37
  • No. There is no way to have mutable and immutable objects in the same hierarchy in any meaningful way. There was a recent question that had a good diagram showing the complexity and an explanation of why that is a bad idea, but it is deleted. Maybe someone else knows of a question to help explain this, I cannot find anything. But I will update my answer to explain a little. – user22815 Jul 10 '15 at 18:43
  • That's kind of a broad statement about immutable queues. I used one just a couple days ago to solve this problem. – Karl Bielefeldt Jul 10 '15 at 18:51
  • @Snowman But that seems to make out that mutable and immutable objects as opposites of each other. I think immutable object are really just objects lacking the capability to mutate. Honestly, the way it is right now is more complex and muddled, because you have to figure out what is a mutable implementation and what is not. It seems to me the only difference between putting all the methods in one interface as opposed to splitting out the mutable methods is one of clarity. – MirroredFate Jul 10 '15 at 18:57
  • @MirroredFate read my most recent edit. – user22815 Jul 10 '15 at 18:58
2

It's basically YAGNI. All the concrete collections in the standard library are mutable, implementing or inheriting the optional operations. They don't care about general purpose immutable collections and neither do the vast majority of Java developers. They're not going to create an entire interface hierarchy just for immutable collections, then not include any implementations.

On the other hand, there are a few special-purpose values or "virtual" collections that could be very useful as immutable, such as empty set, and nCopies. Also, there are third-party immutable collections (such as Scala's), which might want to call existing Java code, so they left the possibility for immutable collections open in the least disruptive way.

  • Ok, that makes some sense. It still seems like it approaches the problem from the wrong direction, though. Why not start by defining immutable collection interfaces, and then define the mutable interfaces for the mutable collection implementations? – MirroredFate Jul 10 '15 at 18:44

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.