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
- be aware that ArrayList has its own, more efficient implementation of spliterator(), and manually override the spliterator() method of his own implementation of List or
- lose a huge deal of performance by using the default method.
So the question is : is it still "as true" that one should prefer composition over inheritance in such situations ?