DISCOVERY

January 16th, 2018

Java 8 Default Method

Java

Java 8

Inheritance

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):

public interface Animal { String aboutMe(); /** * Default method will be called if this method doesn't exist in the implemented class * @return the age of the animal */ default int age() { return 0; } /** * In Java 8 interfaces can have static methods * @return a description of this animal */ static String info() { return "I am an animal"; } }
public interface LivingAnimal extends Animal { void run(); void walk(); void sleep(); void eat(); default String status() { return "I am a living animal!"; } }
public interface Pet extends Animal { String owner(); default String status() { return "I am a pet!"; } }
public class Cat implements LivingAnimal, Pet { private String name; private String owner; public Cat(String name) { this.name = name; } public Cat(String name, String owner) { this.name = name; this.owner = owner; } }
public class Main { public static void main(String... args) { Cat snickers = new Cat("Snickers", "Caroline D"); int age = snickers.age(); System.out.println(age); String status = snickers.status(); System.out.println(status); } }

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:

... @Override public String status() { return Pet.super.status(); }

Now when I run the code age equals 0 and status equals '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 LivingAnimal and 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 age() in 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.

[1] Raoul-Gabriel Urma, Mario Fusco & Alan Mycroft, Java 8 In Action (Shelter Island, NY: Manning, 2015), 214

[2] Ibid., 219

[3] Ibid., 222

[4] Ibid., 223