Java 8 Default Methods Tutorial
In this article we are going to explain how to use and take advantage of the possibility to implement default methods in interfaces. This is one of the most important features that are available since Java update 8.
All examples have been implemented using Eclipse Luna version 4.4 and Java version 8 update 5.
Reasons for default methods in interfaces
In “old” Java, if we want to add new methods to an existing interface, we need to implement these methods in all the classes that are currently implementing this interface. If we do not do this, we are going to get compilation errors and our code (legacy?) is not going to work anymore.
The Oracle team in charge of the Java language development had a similar problem when they wanted to implement the Stream API and other features coming out in the Java update 8.
The Collections Framework Collection
interface, for example, was extended by adding methods like forEach()
, stream()
and parallelStream()
. These methods were added directly to the interface Collection. So in order to make things work they had two options basically:
If a specific class contains an implementation for a default method, this takes preference to the interface one. That means, that interfaces default methods can be overridden in the classes (but do not need to). Default methods can be added to interfaces without any need of changing existing implementing classes.
First examples and syntax
Basically, in order to create a default method in an interface we write something like:
public interface InterfaceWithDefault { public default void defaultMethod() { System.out.println( "I am the default method of the interface " ); } public void toImplementMethod();
In the code above we can see how a default method is implemented in an interface by using the keyword default
.
Diamond problem
Several advantages can be taken with the introduction of default methods in interfaces but, on the other hand, new problems arise: one of this problems is the so called “Diamond problem”, that is, the multiple inheritance problem.
If a class A
extends more than one class B
and C
, and the classes B
and C
, both have the method bc()
implemented, we have a small problem. We need a set of rules to decide what version of the method bc()
is going to be used by the class A
.
This problem was solved until now in Java by preventing multiple inheritance: one class can only extend other class, not more.
But now, with default methods being implemented in interfaces a class X
can implement interfaces Y
and Z
with the default method yz()
. The method yz()
is inherited twice by the class X
, so we have to decide which one to use, the one from Y
or the one from Z
, or no one of them and the class X
has to implement itself the method yz()
.
This last option is the one that Java adopted: that means, a class that implements several interfaces that have the same default method implemented, has to implement this method itself. We are going to explain this with an example.
So we have two interfaces InterfaceAWithDefault
and with a default method defaultMethod()
:
public interface InterfaceAWithDefault { public default void defaultMethod() { System.out.println( "I am the default method of the InterfaceAWithDefault " ); } ... } public interface InterfaceBWithDefault { public default void defaultMethod() { System.out.println( "I am the default method of the InterfaceBWithDefault " ); } ... }
And a class that implements both interfaces:
public class ClassImplementingDefaultInterfaces implements InterfaceAWithDefault, InterfaceBWithDefault { ... }
If we do not implement the method defaultMethod()
in the class ClassImplementingDefaultInterfaces
we would get the following compilation error:
Duplicate default methods named defaultMethod with the parameters () and () are inherited from the types InterfaceBWithDefault and InterfaceAWithDefault
The solution for this error is to implement the method in the implementing class:
public class ClassImplementingDefaultInterfaces implements InterfaceAWithDefault, InterfaceBWithDefault { public void defaultMethod() { System.out.println( "Implemented method... " ); } }
If we want to give preference to one of the interfaces we can call the interface’s implementation:
public class ClassImplementingDefaultInterfaces implements InterfaceAWithDefault, InterfaceBWithDefault { public void defaultMethod() { InterfaceAWithDefault.super.defaultMethod(); } }
In the code above we can see how the interface method is referenced: InterfaceAWithDefault.super.defaultMethod()
using the super
keyword as attribute of the interface name.
Static methods
In combination with default methods, Java 8 offers the possibility to define static methods that can assist the default ones. The following code shows an example of this:
public interface InterfaceWithDefaultAndStatics { public default void defaultMethod() { // it is possible to use interface static methods System.out.println( "I am the default method of the interface, give me five! " + giveMeFive() ); } public static String giveMeFive() { return "5"; } ...
The method giveMeFive()
is static and implemented in the interface. It can be used by other static and default methods inside the interface without any problem.
These static methods are part of the interface and not part of the implementing classes that may implement this interface. Because of that, in order to call these static methods we should prefix them with the interface name, not the class one:
// it is possible to call static methods directly to the interface InterfaceWithDefaultAndStatics.giveMeFive(); // right ClassImplementingDefaultInterface.giveMeFive(); // wrong: The method giveMeFive() is undefined for the type ClassImplementingDefaultInterface
As shown in the snippet of code above, in order to call the interface static method giveMeFive()
we have to prefix it with the interface name, otherwise, if we try to use the class name for this, we would get an error.
Until now, was common to implement static methods in utility classes that were used in several places afterwards. A good and known example for this is the java.util.Collections
class where several static methods related to the interface java.util.Collection
are implemented.
It is not needed any more to implement utility classes to implement there your static methods, you can use interfaces static methods instead.
Object class non final methods
The Object
class contains several methods that are inherited by all classes in Java (more or less). So we can think in providing default customized implementations for these methods by using interfaces default methods. Well, this is just not possible!
For example if we try something like that:
public interface InterfaceWithDefaultsProhibited { @Override public default String toString(){ } }
We will get the following compilation error:
A default method cannot override a method from java.lang.Object
So, it is not permitted to override a method from java.lang.Object in an interface default one.
Abstract classes
Although abstract classes and default methods in interfaces have some points in common, they are not the same concept exactly; here is a list of differences:
Bellow, we have an snippet that show one of the main differences between abstract classes and interfaces (interfaces have no state):
public interface InterfaceDefaultExample { int cachedTwo = -1; public int calculateTwoPlusTwo(); public default int returnTwo() { if( cachedTwo != -1 ) //warning: Comparing identical expressions return cachedTwo; //warning: Dead code cachedTwo = 2; return 2; } }
This code does not compile, the error would be:
The final field InterfaceDefaultExample.cachedTwo cannot be assigned
and also some warnings as we saw in the commented code. If we would use this code in an abstract class, would be no problem, because an abstract class can make use of internal variables and change their state. Here is the code for the abstract class variation:
public abstract class AbstractClassExample { int cachedTwo = -1; public abstract int calculateTwoPlusTwo(); public int returnTwo() { if( cachedTwo != -1 ) return cachedTwo; cachedTwo = 2; return 2; } }
Summary
So, that is all. In this article we saw how add default implementations to interfaces methods, we explained how Java solved the diamond problem related to multiple inheritance, we commented the main differences between abstract methods in abstract classes and default methods in interfaces and we wrote down some examples of all these ones. In general, default methods are one of the new features coming in Java 8 together with Streams, Lambdas or the Date Time API that increase the design flexibility and the code quality; and, for some experts, it is the most important one.
Download the examples
All examples from this article (and some more) can be downloaded in the following link: defaults.
Please take into consideration that some snippets shown in this article are not working and this is their purpose (to show what works and what does not), so not all the attached code compiles.
Links
If you want to find more information about default methods in interfaces or about other Java 8 new features you can visit: