It is usually admitted that extending implementations of an interface through inheritance is not best practice, and that composition (eg. implementing the interface again from scratch) is more maintenable.
This works because the interface contract forces the user to implement all the desired functionality. However in java 8, default methods provide some default behavior which can be "manually" overriden. Consider the following example : I want to design a user database, which must have the functionalities of a List. I choose, for efficiency purposes, to back it by an ArrayList.
public class UserDatabase extends ArrayList<User>{}
This would not usually be considered great practice, and one would prefer, if actually desiring the full capabilities of a List and following the usual "composition over inheritance" motto :
public class UserDatabase implements List<User>{
//implementation here, using an ArrayList type field, or decorator pattern, etc.
}
However, if not paying attention, some methods, such as spliterator() will not be required to be overridden, as they are default methods of the List interface. The catch is, that the spliterator() method of List performs far worse than the spliterator() method of ArrayList, which has been optimised for the particular structure of an ArrayList.
This forces the developer to
So the question is : is it still "as true" that one should prefer composition over inheritance in such situations ?
Before start thinking about performance, we always should think about correctness, i.e. in your question we should consider what using inheritance instead of delegation implies. This is already illustrated by this EclipseLink/ JPA issue. Due to the inheritance, sorting (same applies to stream operation) don’t work if the lazily populated list hasn’t populated yet.
So we have to trade off between the possibility that the specializations, overriding the new default
methods, break completely in the inheritance case and the possibility that the default
methods don’t work with the maximum performance in the delegation case. I think, the answer should be obvious.
Since your question is about whether the new default
methods change the situation, it should be emphasized that you are talking about a performance degradation compared to something which did not even exist before. Let’s stay at the sort
example. If you use delegation and don’t override the default
sorting method, the default
method might have lesser performance than the optimized ArrayList.sort
method, but before Java 8 the latter did not exist and an algorithm not optimized for ArrayList
was the standard behavior.
So you are not loosing performance with the delegation under Java 8, you are simply not gaining more, when you don’t override the default
method. Due to other improvements, I suppose, that the performance will still be better than under Java 7 (without default
methods).
The Stream
API is not easily comparable as the API didn’t exist before Java 8. However, it’s clear that similar operations, e.g. if you implement a reduction by hand, had no other choice than going through the Iterator
of your delegation list which had to be guarded against remove()
attempts, hence wrap the ArrayList
Iterator
, or to use size()
and get(int)
which delegate to the backing List
. So there is no scenario where a pre- default
method API could exhibit better performance than the default
methods of the Java 8 API, as there was no ArrayList
-specific optimization in the past anyway.
That said, your API design could be improved by using composition in a different way: by not letting UserDatabase
implement List<User>
at all. Just offer the List
via an accessor method. Then, other code won’t try to stream over the UserDatabase
instance but over the list returned by the accessor method. The returned list may be a read only wrapper which provides optimal performance as it is provided by the JRE itself and takes care to override the default
methods where feasible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With