Understand functional interfaces

270 words | 1 minute to read

In Java 8 the @FunctionalInterface annotation was introduced, allowing API developers to designate that a particular class is intended for use in lambda expressions. It is not necessary that a class have this annotation, but by having this associated with a class, it enables the compiler to enforce that the class has exactly one abstract method, which is the requirement for supporting lambda expressions.

From a developers point of view this is beneficial as it ensures that a class intended for use in lambda expressions does not accidentally lose that ability with the introduction of additional abstract methods - because the compiler will not allow this situation to arise.

Having said this, developers should have some reticence to creating their own functional interfaces, as recent releases of the JDK ship with 43 functional interfaces baked in, as part of the java.util.function package. These can be broken down into six categories, each outlined below:

Interface Signature
UnaryOperator<T> T apply(T t)
BinaryOperator<T> T apply(T t1, T t2)
Predicate<T> boolean test(T t)
Function<T,R> R apply(T t)
Supplier<T> T get()
Consumer<T> void accept(T t)

Developers can choose to use these functional interfaces in lieu of creating their own, but there are still valid reasons for creating a custom functional interface. Examples include wanting a more descriptive name for the interface (for example, Comparator is a very expressive name for the function it serves), and wanting to introduce additional functionality (default methods in interfaces, or non-abstract methods in classes).