Functional Interfaces (Introduction)
Functional Interfaces (Introduction)
You have already learned that an interface can declare abstract methods that implementing classes must define. A functional interface is simply an interface that declares exactly one abstract method. That single constraint is what makes it special: it means you can represent the entire interface with a single piece of behaviour — a function — and Java can substitute a concise lambda expression wherever that interface is expected.
This lesson covers the concept, the @FunctionalInterface annotation, and why this idea is the foundation of modern Java code.
The Single Abstract Method Rule
Consider this interface:
Transformer has exactly one abstract method. That makes it a functional interface. Before Java 8 you would implement it with an anonymous class:
This works, but it is seven lines of boilerplate to express one idea: convert to uppercase. A functional interface lets Java collapse that entirely.
The @FunctionalInterface Annotation
Add @FunctionalInterface to any interface you intend to keep as a SAM (Single Abstract Method) type:
@FunctionalInterface is a compile-time guard, not a runtime change. If you accidentally add a second abstract method the compiler immediately reports an error: "Invalid '@FunctionalInterface' annotation; Transformer is not a functional interface". Without the annotation the interface still works as a functional interface — the annotation just makes your intent explicit and protected.
Default methods, static methods, and methods inherited from Object (such as equals or toString) do not count as abstract methods for this rule. An interface can have as many of those as it likes and still be a valid functional interface:
Why This Matters: The Road to Lambdas
A functional interface is the target type that a lambda expression satisfies. Java does not have standalone functions — every piece of behaviour must live inside a class or interface. Functional interfaces are the mechanism that lets lambda syntax exist. When Java sees:
it checks: what type is expected on the left? Transformer. Does Transformer have exactly one abstract method? Yes — String transform(String). Does the lambda match that signature (takes a String, returns a String)? Yes. Compilation succeeds and the lambda becomes the implementation of transform.
Java's Built-In Functional Interfaces
The java.util.function package ships with dozens of ready-made functional interfaces so you rarely need to write your own. The four you will see everywhere are:
Function<T, R>— takes aT, returns anR. Abstract method:R apply(T t).Predicate<T>— takes aT, returns aboolean. Abstract method:boolean test(T t).Consumer<T>— takes aT, returns nothing. Abstract method:void accept(T t).Supplier<T>— takes nothing, returns aT. Abstract method:T get().
Here they are in action, all using lambdas as implementations:
Passing Behaviour as a Parameter
The real power emerges when you accept a functional interface as a method parameter, letting callers inject any behaviour they like:
The Filter.keep method knows nothing about the rule; the caller supplies it as a lambda. This is the strategy pattern expressed in two words instead of a class hierarchy.
java.util.function. Function, Predicate, Consumer, and Supplier cover the vast majority of cases. Introducing custom types adds names that collaborators must learn without adding real expressiveness.
Summary
A functional interface has exactly one abstract method. The @FunctionalInterface annotation documents that intent and adds a compile-time check. Because there is only one abstract method, Java can match a lambda expression to it automatically. Java's java.util.function package provides the standard vocabulary (Function, Predicate, Consumer, Supplier) so you rarely write your own. In the next lesson we will dive into lambda syntax in depth and explore method references.