Before looking at Functional Interfaces, it is important to understand their purpose and how they were implemented pre Java 8

Declaration

@FunctionalInterface
interface MyFunctionalInterface {
	String firstClassFunction(String a);
}

Functional Interfaces critically must only have 1 abstract method. This will be the “Function” that the Interface represents./ We can enforce this with the @FunctionalInterface annotation. This, although not compulsory, will tell the compiler that the following Interface should be a Functional Interface and will fail to compile if there are more than 1 abstract methods.

Built-ins

Along with the @FunctionalInterface annotation, Java 8 also included a bunch of Functional Interfaces in the java.util.function package to give us standard ‘function types’ to use. Here are some notables ones:

It also went back and annotated existing Interfaces as Functional Interfaces such as:

Implementation

There’s 2 main ways to implement Functional Interfaces

Lambda Functions

One of the most common ways to use lambdas is in sorting a collection.

We can do this as List<E>’s sort(Comparator<? super E> c) method takes in a Comparator!
Comparator<T> is a Functional Interface with the following abstract function int compare(T o1, T o2);.
Since it is a Functional Interface, we can use a lambda expression! We just need to make sure that our lambda follows the method signature, taking in two variables and returning an int.

// given a collection of Strings
List<String> names = Arrays.asList("Bob", "Alice", "Charlie", "Jack" "Eve", "David");
// sort in-place with a lambda expression to define the sorting criteria
names.sort((a,b) -> a.compareTo(b)); // sort assending 
names.sort((a,b) -> b.compareTo(a)); // sort decending
names.sort((a,b) -> a.length - b.length); //sort by length

Method Reference

Whenever you see really simple lambda expressions, like the ones above that effectively just call a single method. You should use Method References instead! Method references could have better performance as they can easily be directly converted into a MethodHandle, however, modern JVMs are good enough do the same for simple lambdas. Let’s convert the lambdas above to Method References

names.sort(String::compareTo); // sort assending 
// It is not possible to sort decending directly as there is no method for that
names.sort(Comparator.comparing(String::length)); //sort by length

You can also leverage any existing method that matches the functional interface signature, making your code more concise and potentially more performant while improving readability by clearly indicating which operation is being performed.

// Class with custom business methods
public class OrderProcessor {
	// This method happens to match the Predicate Functional Interface signature
    public boolean isValidOrder(Order order) { /* validation logic */ }
    // This method happens to match the ToDoubleFunction Functional Interface signature
    public double calculateDiscount(Order order) { /* discount logic */ }
}
 
OrderProcessor processor = new OrderProcessor();
List<Order> orders = getOrders();
 
// Filter valid orders
List<Order> validOrders = orders.stream()
    .filter(processor::isValidOrder)  // Instead of: o -> processor.isValidOrder(o)
    .collect(toList());
 
// Calculate discounts
List<Double> discounts = orders.stream()
    .mapToDouble(processor::calculateDiscount)  // Instead of: o -> processor.calculateDiscount(o)
    .boxed()
    .collect(toList());