Skip to main content
Java

Functional Interfaces

3 mins

A lighthouse standing alone, symbolizing the singular guiding light of a functional interface, providing a clear and single point of functionality in code.

SAM Interfaces #

Before discussing functional interfaces, let’s first understand the concept of Single Abstract Method (SAM) interfaces. An interface that has exactly one abstract method is called a SAM interface. For example, the Runnable interface is a SAM interface because it has only one abstract method, run().

@FunctionalInterface
public interface Runnable {
    void run();
}

Functional interfaces only allow one abstract method.

The @FunctionalInterface annotation is a new feature introduced in Java 8. While not required, it is used to indicate that an interface is a functional interface that should have only one method. If an interface is annotated with @FunctionalInterface, the compiler will enforce that the interface has only one abstract method. If the interface has more than one abstract method, the compiler will throw an error.

Lambda Expressions #

Functional interfaces are what make lambda expressions work in Java. The lambda expression provides the implementation of the abstract method, and it can be passed around as if it were an object.

This facilitates functional programming in Java by treating code as data, which is a programming paradigm where code (or more specifically, functions) can be manipulated and passed around within a program just like data. This concept is a cornerstone of functional programming and allows for a high degree of flexibility in how functions are used and combined.

Here is an example of a lambda expression that implements the Runnable interface:

public class LambdaExample {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("Hello, World!");
        r.run();
    }
}

Methods References #

Functional interfaces can also be used to reference methods. Method references are a shorthand notation for a lambda expression that calls a method.

Suppose we have a functional interface ValidatePayload with a single abstract method boolean validate(String payload).

interface ValidatePayload {
    boolean validate(String payload);
}

A class that uses the ValidatePayload interface to validate a payload.

public class ProcessPayload {
    String payload;

    public ProcessPayload(String payload) {
        this.payload = payload;
    }

    public void process(ValidatePayload validatePayload) {
        if (validatePayload.validate(payload)) {
            System.out.println("Payload is valid");
        } else {
            System.out.println("Payload is invalid");
        }
    }
}

Prior to Java 8, we would have to create a class that implements the ValidatePayload interface (or an anonymous class) and pass an instance of that class to the process method.

public class ValidateContent implements ValidatePayload {
    public boolean validate(String payload) {
        if (payload == null || payload.isEmpty()) {
            return false;
        }

        if (payload.contains("<main>")) {
            return true;
        } 

        return false;
    }
}

public class Main {
    public static void main(String[] args) {
        ProcessPayload processPayload = new ProcessPayload("<main>1234</main>");
        ValidateContent validateContent = new ValidateContent();
        processPayload.process(validateContent);
    }
}

From Java 8 onwards, we can define the implementation of the validate method separately and pass it to the process using a method reference.

public class Main {
    private static boolean validateContent(String payload) {
        if (payload == null || payload.isEmpty()) {
            return false;
        }

        if (payload.contains("<main>")) {
            return true;
        } 

        return false;
    }

    public static void main(String[] args) {
        ProcessPayload processPayload = new ProcessPayload("<main>1234</main>");
        processPayload.process(Main::validateContent);
    }
}

In the above example, Main::validateContent is a method reference that refers to the validateContent method. The method reference is equivalent to the lambda expression (payload) -> Main.validateContent(payload).

Functional interfaces provide a powerful way to handle single-method implementation scenarios, making code more readable and concise, especially when used with lambda expressions and method references.