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)
- Exactly one abstract method
-
Can contain:
- default methods
- static methods
- Can override Object class methods
- @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