← Back to Home

Functional Interfaces

A functional interface is the foundation of lambda expressions in Java. It represents a single unit of behavior and enables functional programming constructs introduced in Java 8.

This is a very high-frequency interview topic, tightly coupled with lambdas, streams, and method references.

What Is a Functional Interface?

A functional interface is an interface that has exactly one abstract method.

@FunctionalInterface
public interface Runnable {
    void run();
}
          
  • Can have any number of default and static methods
  • Must have only one abstract method

Why Functional Interfaces Were Introduced

  • Enable lambda expressions
  • Reduce boilerplate code
  • Support functional programming style
  • Improve readability and expressiveness

Rules of Functional Interfaces (Interview Must-Know)

  1. Exactly one abstract method
  2. Can contain:
    • default methods
    • static methods
  3. Can override Object class methods
  4. @FunctionalInterface annotation is optional but recommended

Example: Custom Functional Interface

@FunctionalInterface
interface Calculator {
    int add(int a, int b);
}
          

Valid functional interface.

Using Functional Interface with Lambda

Calculator c = (a, b) -> a + b;
System.out.println(c.add(10, 20));
          

Lambda implements the abstract method.

Invalid Functional Interface Example

@FunctionalInterface
interface Invalid {
    void m1();
    void m2(); // ❌ Compilation error
}
          

Reason: more than one abstract method.

Default & Static Methods in Functional Interface

@FunctionalInterface
interface Demo {
    void show();

    default void info() {
        System.out.println("Default method");
    }

    static void help() {
        System.out.println("Static method");
    }
}
          
  • Still functional
  • Only one abstract method matters

Built-in Functional Interfaces (Very Important)

Java provides many functional interfaces in java.util.function.

1️⃣ Predicate<T>

  • Takes one input
  • Returns boolean
  • Used for conditions / filtering
Predicate<Integer> isEven = n -> n % 2 == 0;
          

Method:

boolean test(T t);
          

2️⃣ Function<T, R>

  • Takes one input
  • Returns one output
Function<String, Integer> length = s -> s.length();
          

Method:

R apply(T t);
          

3️⃣ Consumer<T>

  • Takes one input
  • Returns nothing
Consumer<String> print = s -> System.out.println(s);
          

Method:

void accept(T t);
          

4️⃣ Supplier<T>

  • Takes no input
  • Returns output
Supplier<Double> random = () -> Math.random();
          

Method:

T get();
          

5️⃣ Runnable (Classic Functional Interface)

Runnable r = () -> System.out.println("Thread running");
          

Method:

void run();
          

Primitive Functional Interfaces (Performance-Oriented)

Avoid boxing/unboxing.

Interface Example
IntPredicate int → boolean
IntFunction<R> int → R
IntConsumer int → void
IntSupplier → int

Used in performance-critical code.

Functional Interface vs Normal Interface

Aspect Functional Interface Normal Interface
Abstract methods Exactly 1 Multiple
Lambda support ✔ Yes ❌ No
Java version 8+ All
Usage Behavior Contract

Functional Interface vs Abstract Class

Aspect Functional Interface Abstract Class
Inheritance Multiple allowed Single
State ❌ No ✔ Yes
Lambda support ✔ Yes ❌ No
Constructor ❌ No ✔ Yes

Functional Interface & Method References (Preview)

Consumer<String> c = System.out::println;
          
  • Cleaner alternative to lambdas
  • Requires functional interface

Common Beginner Mistakes

  • Adding multiple abstract methods
  • Forgetting functional interface requirement
  • Overusing lambdas for complex logic
  • Confusing default methods with abstract methods
  • Not using built-in interfaces when available

Best Practices (Interview + Real World)

  • Use built-in functional interfaces whenever possible
  • Mark custom ones with @FunctionalInterface
  • Keep lambdas short and readable
  • Prefer method references when clearer
  • Avoid state inside lambdas

Interview-Ready Answers

Short Answer

A functional interface is an interface with exactly one abstract method.

Detailed Answer

In Java, a functional interface is an interface that contains a single abstract method and can be implemented using lambda expressions. It enables functional programming in Java and includes built-in interfaces like Predicate, Function, Consumer, and Supplier.

Key Takeaway

Functional Interfaces enable lambdas. They are the backbone of modern Java, powering streams, concurrency, and functional programming.

Functional Interface Quick Reference (Examples + Traps)

1) What Makes an Interface Functional

@FunctionalInterface
interface Task {
    void execute();
}
          

Rule

  • Exactly one abstract method
  • default / static methods allowed

2) Functional Interface with Lambda

Task t = () -> System.out.println("Task executed");
t.execute();
          

3) ❌ Not a Functional Interface (Trap)

interface Bad {
    void a();
    void b(); // ❌ two abstract methods
}
          

4) @FunctionalInterface Catches Errors

@FunctionalInterface
interface Safe {
    void run();
    // void stop(); // ❌ compile-time error
}
          

5) Predicate<T> – Boolean Result

import java.util.function.Predicate;

Predicate<Integer> isEven = x -> x % 2 == 0;
System.out.println(isEven.test(10)); // true
          

6) Function<T, R> – Transform Input

import java.util.function.Function;

Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Java")); // 4
          

7) Consumer<T> – No Return

import java.util.function.Consumer;

Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello");
          

8) Supplier<T> – No Input

import java.util.function.Supplier;

Supplier<Double> random = () -> Math.random();
System.out.println(random.get());
          

9) BiFunction<T, U, R>

import java.util.function.BiFunction;

BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(3, 4)); // 7
          

10) BiPredicate<T, U>

import java.util.function.BiPredicate;

BiPredicate<String, String> equalsIgnore =
        (a, b) -> a.equalsIgnoreCase(b);

System.out.println(equalsIgnore.test("java", "JAVA")); // true
          

11) UnaryOperator<T> (Same Input/Output)

import java.util.function.UnaryOperator;

UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25
          

12) BinaryOperator<T>

import java.util.function.BinaryOperator;

BinaryOperator<Integer> max = (a, b) -> a > b ? a : b;
System.out.println(max.apply(10, 20)); // 20
          

13) Custom Functional Interface with Parameters

@FunctionalInterface
interface Calculator {
    int calc(int a, int b);
}

Calculator add = (a, b) -> a + b;
System.out.println(add.calc(5, 7)); // 12
          

14) Default Method in Functional Interface

@FunctionalInterface
interface Logger {
    void log(String msg);

    default void info(String msg) {
        log("INFO: " + msg);
    }
}

Logger l = s -> System.out.println(s);
l.info("Started");
          

15) Static Method in Functional Interface

@FunctionalInterface
interface MathOp {
    int apply(int a, int b);

    static int add(int a, int b) {
        return a + b;
    }
}
          

16) Functional Interface with Method Reference

import java.util.function.Consumer;

Consumer<String> c = System.out::println;
c.accept("Method reference");
          

17) Passing Functional Interface as Argument

import java.util.function.Predicate;

class Demo {
    static boolean check(int x, Predicate<Integer> p) {
        return p.test(x);
    }

    public static void main(String[] args) {
        System.out.println(check(10, n -> n > 5)); // true
    }
}
          

18) Returning Functional Interface

import java.util.function.Supplier;

class Demo {
    static Supplier<String> supplier() {
        return () -> "Hello";
    }

    public static void main(String[] args) {
        System.out.println(supplier().get());
    }
}
          

19) Functional Interface with Streams

import java.util.*;

class Demo {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4);
        list.stream()
            .filter(x -> x % 2 == 0)   // Predicate
            .map(x -> x * 10)          // Function
            .forEach(System.out::println); // Consumer
    }
}
          

20) Effectively Final Rule (Trap)

int x = 10;
Runnable r = () -> System.out.println(x);
// x++; // ❌ not allowed
          

21) Functional Interface vs Abstract Class

@FunctionalInterface
interface A { void run(); }

// Abstract class can have state
abstract class B {
    int x;
    abstract void run();
}
          

22) Using Functional Interface in Thread

new Thread(() -> System.out.println("Running")).start();
          

23) Comparator Is a Functional Interface

import java.util.*;

List<Integer> list = Arrays.asList(3, 1, 2);
list.sort((a, b) -> a - b);
System.out.println(list);
          

24) ❌ Lambda with Non-Functional Interface (Trap)

// Interface with 2 abstract methods → lambda not allowed

25) Interview Summary – Functional Interfaces

@FunctionalInterface
          

Key Points

  • Exactly one abstract method
  • Enables lambda expressions
  • Built-ins: Predicate, Function, Consumer, Supplier
  • default / static methods allowed
  • Variables must be effectively final