Functional interfaces in Java
Exactly one abstract method; @FunctionalInterface.
(a, b) -> a + b — no class, no method name, no implements. Yet Java knows exactly which interface it implements. How?One hole, one lambda
A functional interface has exactly one abstract method. A lambda supplies the body for that one method, so Java always knows which method the arrow implements.
@FunctionalInterface
interface Op {
int apply(int a, int b);
}
Op add = (a, b) -> a + b;
add.apply(2, 3); // 5Two lambdas, one interface
What does this print?
interface Op {
int apply(int a, int b);
}
void main() {
Op max = (a, b) -> a > b ? a : b;
Op sub = (a, b) -> a - b;
int r = max.apply(4, 9) + sub.apply(4, 9);
System.out.println(r);
}4149-5
Show the answer
Each lambda is a separate implementation of apply. max gives 9 and sub gives -5; both are ints, so + adds them: 4.
@FunctionalInterface: a safety net
The annotation is optional, but it makes the compiler enforce the one-method rule. Only *abstract* methods count: an interface with one abstract method plus three default or static methods is still functional.
@FunctionalInterface
interface Task {
void run();
default void log() { } // fine
}Two abstract methods? Not functional
Add a second abstract method and @FunctionalInterface turns into a compile error. Interfaces like List<E> (add, get, size…) can never be lambda targets. Runnable, Predicate<T>, Supplier<T>, Function<T,R> and Comparator<T> can.
@FunctionalInterface
interface Task {
void run();
void stop(); // compile error
}The Comparator puzzle
Comparator<T> declares int compare(T a, T b) AND boolean equals(Object o), both abstract. Why is it still a functional interface?
Think about it, then reveal the answer
equals matches a **public method of Object, and every class already inherits that. So abstract methods that redeclare public Object methods don't count**. Only compare is left.
Lambdas everywhere
The java.util.function package (Supplier, Function, Predicate, Consumer…) is what makes streams work: list.stream().filter(s -> s.isEmpty()). You also pass lambdas as Runnable to threads and as Comparator to sort. Each is just one abstract method.
Key takeaways
- Exactly one abstract method (defaults and statics don't count)
- @FunctionalInterface makes the compiler check it
- Built-ins: Runnable, Supplier<T>, Function<T,R>, Predicate<T>, Comparator<T>
- Abstract methods matching public Object methods (like equals) don't count
Runnable (1996) and Comparator (1998) became lambda targets in 2014 with Java 8 — without changing their methods. They had exactly one abstract method all along.
Practice questions
What does this print?
@FunctionalInterface
interface Op {
int apply(int a, int b);
}
void main() {
Op add = (a, b) -> a + b;
Op mul = (a, b) -> a * b;
System.out.println(add.apply(2, 3) + mul.apply(2, 3));
}- 11
- 5 6
- 56
- Compile error
Check your answer
11. Each lambda implements apply. add gives 5 and mul gives 6; both are ints, so + adds them to 11.
What does this print?
@FunctionalInterface
interface Task {
void run();
void stop();
}
void main() {
System.out.println("ok");
}- ok
- Compile error
- Throws IllegalStateException
Check your answer
Compile error. The annotation asks the compiler to verify there's exactly one abstract method. Task has two, so compilation fails.