Before Java 8, interfaces could not have default
nor static
methods, hence, every method added to the interface needed to be implemented by all classes implementing the interface. Even when you provided a helpful implementation in a support class, the implementing class needed at least a delegating method.
So, unless you wanted to force all implementations to inherit from some abstract base class providing those methods, you had to be careful with what you add to the interface.
Even with the default
methods, you have to be careful, to avoid polluting the name space of an implementing class with too many methods. This might also be the reason why not every operation has been retrofitted to default
methods in Java 8:
While adding a default
method to an interface is less intrusive, as it does not create the need to implement it in already existing implementation classes, it may still cause a clash with a concrete method of the implementation class that didn’t implement an interface method in the previous version.
Just imagine a custom List
implementation in pre-Java 8 times that provided a helpful void sort(Comparator c)
instance method just delegating to Collections.sort(this, c);
. That worked before Java 8, not improving the performance, but allowing to write list.sort(c);
. Nowadays, this method would unintentionally happen to override the default
method with the same name and type, to which Collections.sort
will delegate, producing an infinite loop (or rather, recursion).
Still, the sort
method has been added to the List
interface, because of the immediate benefits. Unlike the static
methods in the Collections
utility class, the default
method can be overridden. This has been done for the most commonly used list types, ArrayList
, Vector
, and the implementation returned by Array.asList(…)
. Since all those implementations are backed by an array, the overriding method can delegate to Arrays.sort
using the backing array directly, whereas the default
implementation will work with a temporary copy of the list contents.
It’s also worth noting that those methods in Collections
seem to be originally based on the assumption that those algorithms were suitable to all kind of implementations, which didn’t hold. Two releases after the introduction of the Collection API, the RandomAccess
marker interface was introduced, to tell two fundamentally different list implementation categories apart, so the static method could branch to two alternative algorithms based on it.
Whenever we have to branch based on the class we’re operating on, we could question the abstraction and say that we might be better off with overridable methods on the type itself, but as explained above, there are historical reasons for the design and there still are reasons to be careful with adding methods to the interface.