Functional Interfaces
A functional interface is an interface that declares exactly one abstract method. That single abstract method is what a lambda expression or method reference implements. A functional interface can still declare any number of default and static methods — those do not count toward the “one abstract method” rule, because they already have a body.
The @FunctionalInterface annotation
Java lets you mark an interface with @FunctionalInterface. The annotation itself does not change how the interface behaves — it is optional, and a lambda works fine on an unannotated single-method interface. Its real value is as a safety net: if you (or a future teammate) accidentally add a second abstract method to an annotated interface, the compiler raises an error immediately instead of letting existing lambda-based code silently break.
A custom functional interface
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
// Allowed — default methods don't count as abstract methods
default Calculator andThenDouble() {
return (a, b) -> calculate(a, b) * 2;
}
}
public class Main {
public static void main(String[] args) {
Calculator add = (a, b) -> a + b;
System.out.println(add.calculate(3, 4)); // 7
System.out.println(add.andThenDouble().calculate(3, 4)); // 14
}
}If a second abstract method like int calculateTwice(int a); were added to Calculator above, the @FunctionalInterface annotation would make the compiler reject the interface right away, with a clear error message — without the annotation, the mistake would only surface later, wherever a lambda tried (and failed) to implement the now-ambiguous interface.
The built-in functional interfaces
You rarely need to declare your own functional interface for common shapes — the java.util.function package ships a standard library of them, used throughout the Stream API and elsewhere.
Interface | Abstract method | Purpose |
|---|---|---|
|
| Takes a T, returns an R — a transformation |
|
| Takes a T, returns a boolean — a yes/no test |
|
| Takes a T, returns nothing — an action performed on a value |
|
| Takes nothing, returns a T — a source of values |
|
| Takes a T and a U, returns an R — a two-argument transformation |
One example of each
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("hello")); // 5
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true
Consumer<String> printer = s -> System.out.println("Got: " + s);
printer.accept("world"); // Got: world
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get()); // some random double
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
System.out.println(multiply.apply(6, 7)); // 42A functional interface has exactly one abstract method;
defaultandstaticmethods are unlimited.@FunctionalInterfaceis optional but recommended — it makes accidentally adding a second abstract method a compile error.java.util.functionships common shapes:Function,Predicate,Consumer,Supplier,BiFunction, and more.Lambdas and method references can only target functional interfaces.