Extending Interfaces by combining Default Methods and Lambda Expressions

Java 8 Lambda Expressions and Interface Default Methods can be combined to add methods to existing interfaces. This works somewhat like traits in Scala.

Here's a simple example of combining Lambda Expressions and Default Methods to add methods to an interface:

package com.alainodea;

import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        EQ<String> stringEQ = String.CASE_INSENSITIVE_ORDER::compare;
        System.out.printf("stringEQ.lessThan(\"a\", \"b\") = %b%n",
            stringEQ.lessThan("a", "b"));
        System.out.printf("stringEQ.greaterThan(\"a\", \"b\") = %b%n",
            stringEQ.greaterThan("a", "b"));
    }

    public interface EQ<T> extends Comparator<T> {
        default Ordering compareEq(T o1, T o2) {
            return Ordering.fromCompare(compare(o1, o2));
        }

        default boolean lessThan(T o1, T o2) {
            return Ordering.LT.equals(compareEq(o1, o2));
        }

        default boolean greaterThan(T o1, T o2) {
            return Ordering.GT.equals(compareEq(o1, o2));
        }
    }

    public enum Ordering {
        LT, EQ, GT;

        static Ordering fromCompare(int comparison) {
            return comparison < 0 ? LT : comparison == 0 ? EQ : GT;
        }
    }
}

In this case we add  lessThan and  greaterThan to the interface and replace the use of  int as the type with a specific  Ordering enum.  The essence of this happens in the assignment statement:

    EQ<String> stringEQ = String.CASE_INSENSITIVE_ORDER::compare;

The Lambda Expression autotyping  upgrades the interface to include your added methods, but requires that:

  1. The interface you are extending is a  functional interface .
  2. Your interface declaration must declare all its methods to be  default and provide implementations.

Java 8 essentially allows you to locally add methods to interfaces via assignment due to the auto-typing of lambda expressions.  Under the hood it clearly creates the bridging, but it requires much less boilerplate than would have been required in Java 7 or earlier.

This is particularly powerful when, as above, the new methods are defined in terms of existing methods of the interface being extended.  It's still useful otherwise, but less sound from a software design perspective.

Very simple to achieve and applicable to many other scenarios.