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 s
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
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.