In the new java 8 specification language. They introduced the default modifier that allows the definition of a method in an interface.
My question is, does anyone have a good idea what's the best use case of this feature ?
In the new java 8 specification language. They introduced the default modifier that allows the definition of a method in an interface.
My question is, does anyone have a good idea what's the best use case of this feature ?
Typical example is its use in the Iterable
interface where a default stream()
method has been introduced in Java 8. That way all Iterable
s inherit an already impemented stream
method automagically. This gives support for API evolution without breaking the already existing code.
Let's say you create a new Collection framework, it would probably be reasonable to define:
interface MyCollection {
int size();
default boolean isEmpty() { return size() == 0; }
}
By doing that, you save the pain of having to redefine isEmpty
exactly the same way in all implementing classes, while allowing a specific class to implement it differently if required.
That could be done in an abstract class but this enables you to remove one layer in the hierarchy and you get the inheritance flexibility of interfaces: a class can inherit several interfaces but can only inherit one abstract class.
See also: Interface with default methods vs Abstract class in Java 8.
I believe that the main reason why they introduced this feature was to allow the upgrade of the very elementary interfaces - Collection
, Iterable
, Comparable
, ... without inducing an incompatibility to already existing code - as assylias mentioned in his first paragraph.
Furthermore, you may reach very interesting result by method chaining - methods which return the same type and you main chain they calls:
// Multiples of 5
final Predicate<Integer> fives = n -> n%5==0;
// Multiples of 7
final Predicate<Integer> sevens = n -> n%7==0;
// Multiples of 3
final Predicate<Integer> threes = n -> n%3==0;
// Multiples of 5 and 7 but not 3
final Predicate<Integer> compoundPredicate = fives.and(sevens).and(threes.negate());
This is possible due to extensive use of default methods:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
return (t) -> test(t) && other.test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
return (t) -> test(t) || other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
}
As for the default behaviour, this will not only remove a completely artificial layer of abstract class (which would be created purely for the default behaviour), but it gives you the opportunity to introduce really useful abstract classes which model actual is-a relationships.
See the following example motivated by a game implementation. For most of the (monsters, isDead()
is just a convenience method. But a zombie is never dead, isn't it. :)