Since Java 8 we can write implementations into interfaces. As several interfaces can be implemented by a class, a class can now inherit actual code (not just responsibilities) from multiple different sources. Such inherited default implementations are overridable, and multiple-inheritance problems will detected and rejected by the Java 8 compiler.
Example
The interface Identified
demands the presence of an identifier like a database primary key.
The interface Named
demands the presence of a human readable name.
Objects of the example class Person
should be both identified and named.
public static void main(String[] args) { Person person = new Person(1L, "John Doe"); System.out.println("Person id="+person.getId()+", name="+person.getName()); }
This should output:
Person id=1, name=John Doe
Before Java 8
It was not possible to provide a default implementation of an interface responsibility, all implementations had to be in the class:
public interface Identified { Object getId(); }
public interface Named { String getName(); }
public class Person implements Identified, Named { private final Object identity; private final String name; public Person(Object identity, String name) { this.identity = identity; this.name = name; } @Override public Object getId() { return identity; } @Override public String getName() { return name; } }
Since Java 8
In case the inherited interface responsibility happens to be optional, we can now provide a default implementation for any interface method:
public interface Named { default String getName() { return "(undefined name)"; } }
public interface Identified { default Object getId() { return null; } }
As both interfaces provide defaults, the inheriting class is not required to implement anything:
public class Person implements Identified, Named { }
This compiles!
Output will be:
Person id=null, name=(undefined name)
Overriding Defaults
Most important here: default
implementations can be overridden (other than static ones).
In the Person
example this actually makes sense.
Following source shows an implementation that redirects getId()
to the according constructor parameter:
public class Person implements Identified, Named { private final Object identity; public Person(Object identity) { this.identity = identity; } @Override public Object getId() { return identity; } }
Conflict Solution
The "diamond problem" occurs in case two inherited interfaces provide a default method of same signature:
public interface Identified { default boolean isDeleted() { return false; } .... }
public interface Named { default boolean isDeleted() { return true; .... }
The isDeleted()
method has been introduced on both interfaces,
with different default implementations.
Which one would be used at runtime by Person
?
We don't need to think about this.
Following error message is printed by the Java compiler when it comes to class Person
:
Duplicate default methods named isDeleted with the parameters () and () are inherited from the types Named and Identified.
Following override would fix it:
public class Person implements Identified, Named { .... @Override public boolean isDeleted() { return Identified.super.isDeleted(); } }
The class decides for one of the defaults, in this case for
the implementation in interface Identified
.
Conclusion
Interface default implementations could easily take over the role
that
static utility classes
have been playing until now (StringUtils
, IOUtils
, ...).
Theses classes provided "pure"
method implementations that rely on
parameters only, without using their enclosing class body in any way.
The problem with these utils is that they are not overridable.
Interface default methods are overridable and would solve this problem.
Method signature clashes would be found by the compiler.
Keep in mind that interfaces still can not hold any state, that means default implementations need to be "pure", everything they need has to be passed to them as parameter. But an interface now is the perfect place for "pure" functions.
Mind further that interfaces methods are always public
, implicitly.
You can not reduce their visibility to protected
or private
in an override.