A functional interface is any interface with exactly one abstract method (SAM — Single Abstract Method). @FunctionalInterface is a compile-time check that asserts this property, so accidentally adding a second abstract method becomes a build error instead of breaking every lambda call site. It also documents intent to readers and tools.
The definition
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t); // the single abstract method
default Predicate<T> and(Predicate<T> other) { // OK, default doesn't count
return v -> test(v) && other.test(v);
}
static <T> Predicate<T> isEqual(Object target) { // OK, static doesn't count
return target == null ? Objects::isNull : target::equals;
}
}
What counts as "abstract" for the SAM rule:
defaultmethods don't count.staticmethods don't count.privatemethods don't count.- Methods that override
Objectmethods (equals,hashCode,toString) don't count.
Only "new" abstract methods that an implementer must supply count toward the SAM total.
What the annotation buys you
Without @FunctionalInterface:
public interface Calculator {
int compute(int a, int b);
void log(String msg); // someone adds this later
}
Calculator add = (a, b) -> a + b; // compile error: not a functional interface
You'd discover the breakage at every call site that used a lambda. With @FunctionalInterface, the error happens at the interface declaration — you fix it once and lambdas keep working (or you intentionally promote it to a non-functional interface).
@FunctionalInterface
public interface Calculator {
int compute(int a, int b);
void log(String msg); // compile error: multiple non-overriding abstract methods
}
The Object-methods exception
@FunctionalInterface
public interface Comparator<T> {
int compare(T a, T b); // SAM
boolean equals(Object o); // re-declared from Object — doesn't count
}
Comparator.equals is declared so the interface can document a stricter contract (two comparators are equal only if they impose the same ordering), but it doesn't add a second abstract method — implementers already have equals from Object.
Why lambdas need SAM
A lambda is shorthand for an instance of a functional interface. The compiler infers which method the lambda implements from the target type. With multiple abstract methods, the inference is ambiguous — there's no way to know which one your lambda body is supposed to be.
Runnable r = () -> System.out.println("hi"); // -> Runnable.run
Callable<Integer> c = () -> 42; // -> Callable.call
Both Runnable and Callable are functional interfaces with one abstract method each. The lambda becomes the body of that one method; the variable type tells the compiler which.
Common functional interfaces in java.util.function
Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>, BiFunction<T,U,R>, UnaryOperator<T>, BinaryOperator<T>, plus primitive-specialized versions (IntFunction, ToIntFunction, etc.) to dodge boxing in hot loops.