Functional Interfaces

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • A Functional Interface is an interface that contains exactly one abstract method.
  • They are the foundation of Java’s Lambda Expressions and Streams API.
  • The @FunctionalInterface annotation is optional but recommended to enforce the single-method rule at compile time.

Concept

In Java, passing behavior (a function) to another method used to be incredibly verbose using Anonymous Inner Classes.
Java 8 introduced Lambdas (->) to solve this. But Java is strongly typed; what is the “Type” of a Lambda expression?
The answer is a Functional Interface. A Lambda is simply a shorthand, dynamic implementation of that single abstract method.

(Note: A Functional Interface can have infinite default or static methods, as long as there is only ONE abstract method).

Examples

// 1. Define a Functional Interface
@FunctionalInterface
public interface MathOperation {
    int operate(int a, int b); // Exactly ONE abstract method
    
    // Default methods are perfectly fine!
    default void printResult(int result) {
        System.out.println("Result: " + result);
    }
}

public class FunctionalDemo {
    public static void main(String[] args) {
        
        // 2. Use a Lambda expression to implement the interface on the fly
        MathOperation addition = (a, b) -> a + b;
        MathOperation multiplication = (a, b) -> a * b;
        
        addition.printResult( addition.operate(5, 3) ); // 8
        multiplication.printResult( multiplication.operate(5, 3) ); // 15
    }
}

Interview Questions

Q: What are the core built-in Functional Interfaces in java.util.function?
A: Java provides dozens, but they stem from four main types:

  1. Predicate<T>: Takes an input, returns a boolean (Used in stream.filter()).
  2. Function<T, R>: Takes an input, transforms it, returns a result (Used in stream.map()).
  3. Consumer<T>: Takes an input, returns nothing/void (Used in stream.forEach()).
  4. Supplier<T>: Takes no input, returns a value (Used in Optional.orElseGet()).

Q: Why is the @FunctionalInterface annotation used?
A: It is purely a compiler safeguard. An interface with exactly one abstract method is technically a functional interface even without the annotation. However, if you add the annotation, the compiler will aggressively throw an error if another developer accidentally tries to add a second abstract method to it later, preventing them from breaking all the Lambda expressions across the codebase that rely on it.