A great use-case are what I call "lever" interfaces: interfaces that only have a small number of abstract methods (ideally 1), but provide a lot of "leverage" in that they provide you with a lot of functionality: you only need to implement 1 method in your class but get a lot other methods "for free". Think of a collection interface, for example, with a single abstract foreach
method and default
methods like map
, fold
, reduce
, filter
, partition
, groupBy
, sort
, sortBy
, etc.
Here are a couple of examples. Let's start with java.util.function.Function<T, R>
. It has a single abstract method R apply<T>
. And it has two default methods that let you compose the function with another function in two different ways, either before or after. Both of those composition methods are implemented using just apply
:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
return (T t) -> after.apply(apply(t));
}
You could also create an interface for comparable objects, something like this:
interface MyComparable<T extends MyComparable<T>> {
int compareTo(T other);
default boolean lessThanOrEqual(T other) {
return compareTo(other) <= 0;
}
default boolean lessThan(T other) {
return compareTo(other) < 0;
}
default boolean greaterThanOrEqual(T other) {
return compareTo(other) >= 0;
}
default boolean greaterThan(T other) {
return compareTo(other) > 0;
}
default boolean isBetween(T min, T max) {
return greaterThanOrEqual(min) && lessThanOrEqual(max);
}
default T clamp(T min, T max) {
if (lessThan( min)) return min;
if (greaterThan(max)) return max;
return (T)this;
}
}
class CaseInsensitiveString implements MyComparable<CaseInsensitiveString> {
CaseInsensitiveString(String s) { this.s = s; }
private String s;
@Override public int compareTo(CaseInsensitiveString other) {
return s.toLowerCase().compareTo(other.s.toLowerCase());
}
}
Or an extremely simplified collections framework, where all collections operations return Collection
, regardless of what the original type was:
interface MyCollection<T> {
void forEach(java.util.function.Consumer<? super T> f);
default <R> java.util.Collection<R> map(java.util.function.Function<? super T, ? extends R> f) {
java.util.Collection<R> l = new java.util.ArrayList();
forEach(el -> l.add(f.apply(el)));
return l;
}
}
class MyArray<T> implements MyCollection<T> {
private T[] array;
MyArray(T[] array) { this.array = array; }
@Override public void forEach(java.util.function.Consumer<? super T> f) {
for (T el : array) f.accept(el);
}
@Override public String toString() {
StringBuilder sb = new StringBuilder("(");
map(el -> el.toString()).forEach(s -> { sb.append(s); sb.append(", "); } );
sb.replace(sb.length() - 2, sb.length(), ")");
return sb.toString();
}
public static void main(String... args) {
MyArray<Integer> array = new MyArray<>(new Integer[] {1, 2, 3, 4});
System.out.println(array);
// (1, 2, 3, 4)
}
}
This becomes very interesting in combination with lambdas, because such a "lever" interface can be implemented by a lambda (it is a SAM interface).
This is the same use-case that Extension Methods were added for in C♯, but default methods have one distinct advantage: they are "proper" instance methods, which means they have access to private implementation details of the interface (private
interface methods are coming in Java 9), whereas Extension Methods are only syntactic sugar for static methods.
Should Java ever get Interface Injection, it would also allow type-safe, scoped, modular monkey-patching. This would be very interesting for language implementors on the JVM: at the moment, for example, JRuby either inherits from or wraps Java classes to provide them with additional Ruby semantics, but ideally, they want to use the same classes. With Interface Injection and Default Methods, they could inject e.g. a RubyObject
interface into java.lang.Object
, so that a Java Object
and a Ruby Object
are the exact same thing.
java.util.function.Function
for usage of default methods in a brand-new interface.