I found an interesting quote in my book with which I learn Java:

Manche Methoden der in diesem Kapitel beschriebenen Schnittstellen sind in der Dokumentation als optional gekennzeichnet. Bei manchen Collections-Klassen führt die Nutzung solcher Methoden zu einer UnsupportedOperationException . Sie dürfen sich also nicht darauf verlas- sen, dass jede Collections-Klasse alle Methoden der genutzten Schnitt- stellen tatsächlich implementiert.

From "Java Einführung" by Michael Kofler

I try to translate it:

Some methods of interfaces described in this chapter are marked as optional in the documentation. The usage of these methods will lead to a unsupportedOperationException. So you should not think that every Collections-class implements all methods of the interfaces.

I learned that not implementing a method of a abstract class or interface leads to a compilation error. So how is this possible?

  • Take a look here – Laiv Feb 15 '18 at 15:55

These methods are implemented, but the implementation just is

throw new UnsupportedOperationException();

Thus, formally, there is an implementation. But whenever the method is called, the exception is thrown.

  • 4
    Yes. This is, to my mind, a bad practice, but probably at the time when the collections frameworks was invented, the authors did not have time to nicely separate various aspects of collections into statically-checkable interfaces (like MutableCollection, ExpandableCollection, etc), so they added a general interface for all, and put dummy plugs here and there. – 9000 Feb 13 '18 at 20:22
  • 3
    @9000 And of course, there could have some "combinatoric explosion" of interfaces/classes, as you could e. g. have a List which does not allow deleting items, but replacing existing ones, and with both of that you could have a list which allows appending or not. And similar for sets and maps. And then you could have all of these thread safe and not thread safe, for sets and maps there would be several implementations (hash set, tree set, ...) etc. – FrankPl Feb 13 '18 at 20:26
  • 4
    Indeed, it's not an easy problem, there was not a nice and obvious solution that the original authors of java.util.Collection missed. Choosing the level of static-check granularity, they chose the lowest possible level. With Java not having generics by then, they were not sacrificing a lot of static checks around collections anyway. Now that 20 years passed, we may have different preferences. – 9000 Feb 13 '18 at 21:24
  • @9000 And - theory aside - in practice, this tends not to b a major problem: Most of the time the average developer does not write general library code, but knows more or less what is can be expected from a collection being passed as an argument and especially if this collection supports write access or not. And errors caused by some or all write methods throwing exceptions can be detected easily by unit tests. – FrankPl Feb 14 '18 at 17:48
  • This very much depends on which problems do you consider major, and how much do you test. That is, a language like Rust or Haskell forms different expectations of static correctness checks than a language like Python or Javascript. Both kinds are used, but in the latter case you basically write tests for cases that a compiler could have checked. Java now leans more and more to the strict static check camp; UnsupportedOperationException is a holdover from days when it was less so, and can go against today's expectations of proper design. – 9000 Feb 14 '18 at 17:58

These methods are implemented, but they are implemented as throwing an exception. So for example:

public void remove(T object) {
  throw new UnsupportedOperationException();

This is considered bad design. An interface should not contain operations that only some consumers of that interface will use. This is indicated by the Interface Segregation Principle. However, this is the design Java has and it is not possible to remove these methods in a backwards-compatible way.

Java has a number of other weird design decisions around interfaces, such as the Cloneable marker interface although every Object already has a clone() method.

  • 1
    You could also mention the Liskov substitution principle which this design violates. – Vincent Savard Feb 13 '18 at 20:21
  • @VincentSavard Not necessarily. E.g. java.util.Iterator#remove() is explicitly documented as being an optional operation, and throwing this exception if the operation is not provided. Therefore, throwing this exception does not violate the behavioural contract of the method. – amon Feb 13 '18 at 20:23
  • You are correct, I should have said "which this design may violate". That being said, since the question does not mention a specific interface or method, I do believe it would at least warrant a warning that not implementing a method may result in a violation of the LSP. Maybe these comments are sufficient as a warning though! – Vincent Savard Feb 13 '18 at 20:28
  • 2
    @user949300 Absolutely, success has little to do with good design. I stand by my “bad design” judgement as a “don't try this at home!” warning. Note that despite its flaws, the Java standard library was miles ahead of other languages at the time. Notably, the C++ standard library didn't even exist yet (and it solves many problems fundamentally differently, through templates rather than inheritance). Much later, the Scala standard library has a much “better” design, but it's also far more complex and relies on a more advanced type system. – amon Feb 13 '18 at 21:28
  • 1
    @amon Paraphrasing a Churchill quote, I'd say that "it is the worst possible design, except for all the others". :-) – user949300 Feb 13 '18 at 22:51

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