<code>@FunctionalInterface</code> in Java
@FunctionalInterface declares that an interface has exactly one abstract method. The compiler enforces this β if you add a second abstract method, compilation fails. The annotation is optional (any interface with one abstract method can be used as a lambda target), but making it explicit documents intent and catches future mistakes.
Declaring one
@FunctionalInterface
public interface Mapper<T, R> {
R apply(T input);
}
// Usage β a lambda is an instance of a functional interface
Mapper<String, Integer> len = s -> s.length();
len.apply("hello"); // 5
What counts as "one abstract method"
- Exactly one method without a body (the SAM β Single Abstract Method).
defaultmethods don't count.staticmethods don't count.- Methods inherited from
Object(equals,hashCode,toString) don't count.
@FunctionalInterface
public interface Named<T> {
String name(); // the SAM
default String pretty() { return "~ " + name() + " ~"; } // OK β default
static Named<Object> constant(String n) { return () -> n; } // OK β static
boolean equals(Object o); // OK β from Object
}
Common JDK functional interfaces
| Interface | Method | Typical use |
|---|---|---|
Function<T,R> | R apply(T) | stream.map(...) |
BiFunction<T,U,R> | R apply(T,U) | map.merge(...) |
Consumer<T> | void accept(T) | list.forEach(...) |
Supplier<T> | T get() | Optional.orElseGet(...) |
Predicate<T> | boolean test(T) | stream.filter(...) |
UnaryOperator<T> | T apply(T) | list.replaceAll(...) |
Runnable | void run() | Threads, Executors |
Comparator<T> | int compare(T,T) | list.sort(...) |
When to declare your own
When the built-in types don't describe your domain well β for example, a RetryPolicy, a PriceRule, a Validator. Giving the interface a meaningful name makes call sites self-documenting compared to Function<Order, Decision>.
Method references
Function<String, Integer> a = String::length; // unbound instance
Consumer<Object> b = System.out::println; // bound instance
Supplier<User> c = User::new; // constructor reference
Function<Integer, int[]> d = int[]::new; // array constructor
Common mistakes
- Forgetting
@FunctionalInterfaceβ your interface still works as a lambda target today; a future second abstract method silently breaks every caller. - Extending
Functionto rename it β adds a type, doesn't add value. Use a type alias pattern only when the semantics differ. - Overloading generic functional methods β callers can't tell which type the lambda infers; resolution becomes ambiguous.
Related
Pillar: Java annotations. See also interfaces, methods.