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 ?
public class UserDatabase{ List<User> aList = new ArrayList<>(); //Other code}
– OperativeArrayList
spliterator. – FerousArrayList
has a faster implementation ofsplititerator
affect your decision to chose between composition over inheritance? What has implementation details got to do with choosing between inheritance and composition? What hasdefault
methods got to do with choosing between the two? By relying on implementation details for choosing composition, you kind of defy the whole purpose of composition in the first place. Don't confuse yourself. Keep it simple silly. – RaspberryArrayList
, why couldn't thespliterator()
method ofUserDatabase
delegate to thespliterator()
method of the underlyingArrayList
? – Parliamentariandefault
method has been added after the delegation code has been written. So thespliterator()
is only an example to discuss the design pattern in general. – Tripe