Java was not originally designed to support multiple inheritance of implemented methods. Because of this, a class could only extend one other class. However, multiple inheritance could be simulated by implementing multiple interfaces. The only catch was the methods in the interface had no body and had to be created in the implementing class.
This design helped avoid multiple inheritance issues, such as the diamond problem. However, using interfaces for APIs is far from perfect.
Let's say you need to change an API that uses an interface. When adding a new method definition to an interface, all classes that implement this interface must also implement the new method. If all the implementing classes of this interface belong to you its is an easy fix. Unfortunately in the case of public APIs its likely that someone else has a class that implements your interface. Adding a new method to the interface breaks their code!
Java 8 has a fix for this issue called default methods. With default methods, interface methods with the
default keyword contain bodies. If an interface method isn't implemented in the concrete class and a default method exists, the default method in the interface is invoked! With default methods existing code doesn't break on interface changes.
But wait! What's the difference between an abstract class and an interface with default methods? Although both can have method bodies now, there are still differences between the two. First off a class can still only extend one abstract class but can implement multiple interfaces. Second, abstract classes can have instance variables while interfaces can't1.
What about multiple inheritance now that you can inherit methods with implementations from different interfaces? With Java 8 new rules were added to deal with situations where a class has multiple method bodies to choose from.
First off, if one method body comes from a class and the rest come from default methods, the classes implementation is picked. Second, if multiple default methods exist on different levels of the inheritance chain, the implementation in the interface closest to the class is picked. If neither of these conditions are met, the class has to explicitly select which method body to use2.
Lets observe an example of default methods and what occurs in a diamond problem scenario. The interface hierarchy below represents animals:
Here is the code for each entity in the diagram (side note: you can also specify
static method implementations in interfaces with Java 8):
When I tried running this code I got the following error:
This error occurred because I forgot to explicitly select which
status() method body to implement in the
Cat class. Let's fix this issue and explicitly call the
Pet interface implementation3:
Now when I run the code
'I am a pet!'.
For some of us this may be the expected result. For others with diamond problem experience an error may be expected. Although I don't really know C++ (yet...) I know that having diamond inheritance like this example will cause an error. The reason why Java doesn't complain here has to do with the way Java is implemented. Instead of both
Pet having a copy of the default method
age() defined in
Animal as they would in C++, Java knows there is only one default method
Animal4. Therefore no conflict exists!
Working with API's got a lot easier in Java 8. You can check out all the code for this discovery on GitHub.