You’ve probably heard of a functional interface before, often in the same breath as lambdas. What are functional interfaces, and why are they used with lambdas?
Lambdas and functional interfaces go hand in hand in Java.
A functional interface is simply an interface that has a single abstract method. Before Java 8, they were referred to as SAM interfaces.
Why are these interfaces so important that they have their own term? Two reasons:
- Good design.
The “I” in the SOLID design principles refers to the Interface Segregation Principle. This states that many small interfaces are better than one large general purpose interface. Clients shouldn’t be forced to implement interfaces they don’t use. So functional interfaces are the ultimate small interface. - Lambdas.
The only way the compiler can infer which method you’re trying to implement with a lambda expression is that there is only a single method that you can implement.
Quick recap
Let’s revisit the Comparator
example from the previous post.
Using the anonymous inner class code, it’s obvious what you’re doing: you’re implementing the compare()
method:
// use an anonymous inner class to create a Comparator object TreeSetts = new TreeSet<>( new Comparator () { @Override public int compare(Employee e1, Employee e2) { return e1.getAge() - e2.getAge(); } } );
But when you’re using a lambda expression, what can the compiler infer? The only way it can work out what you’re trying to do, is if you’re only allowed to implement a single method.
// use an lambda to create a Comparator object TreeSetts = new TreeSet<>( (Employee e1, Employee e2) -> { return e1.getAge() - e2.getAge(); } );
The Comparator
interface is a functional interface, with the compare()
method being the only abstract method.
@FunctionalInterface annotation
The @FunctionalInterface
annotation marks the interface as a functional interface. It forces the compiler to check that there is only one abstract method.
At first this seems unnecessary. Interfaces are usually very small Java classes and it should be easy to just see what’s in the interface. But from Java 8, we’re allowed to have implemented methods in an interface. These are static and default methods. If there are a lot of methods, it is difficult for a programmer to check that they only have one abstract method, or haven’t forgotten to implement a method appropriately.
For example:
@FunctionalInterface public interface MyInterface { public void foo(); // single abstract method // default instance method - can be overridden if needed public default void bar() { // appropriate code } // static class method - called as MyInterface.baz() public static void baz() { // appropriate code } }
Functional interfaces as targets for lambda expressions
As a general rule, you can’t reuse lambda expressions. If you pass a lambda expression to a method, you would usually copy-and-paste it if you wanted to use it again.
Functional interfaces to the rescue! Variables of functional interface types can be used as targets for lambda expressions. This means that you can assign a lambda expression to a variable, as long as that variable’s type is a functional interface.
Revisiting the Comparator
example, we can reuse the lambda expression easily if we assign it to a functional interface type:
Comparatorcmp1 = (Employee e1, Employee e2) -> { return e1.getAge() - e2.getAge(); } Comparator cmp2 = (e1, e2) -> { return e1.getAge() - e2.getAge(); } Comparator cmp3 = (e1, e2) -> e1.getAge() - e2.getAge(); TreeSet ts = new TreeSet<>(cmp3);
I’ll cover the default and static methods of Comparator
in a later post.