Functional Interfaces in Java
Learn functional interfaces, the
@FunctionalInterface annotation,
built-in interfaces from java.util.function,
lambda expressions, method references, and functional-style
programming in Java.
What is a Functional Interface?
A functional interface is an interface that contains exactly one abstract method.
Functional interfaces are the target types for lambda expressions and method references.
interface Calculator {
int calculate(int a, int b);
}
@FunctionalInterface Annotation
The @FunctionalInterface annotation tells the
compiler and other developers that an interface is intended
to be a functional interface.
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
If another abstract method is added, the compiler reports an error because the interface no longer satisfies the functional interface contract.
Functional Interface with Lambda
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
Calculator addition =
(a, b) -> a + b;
int result =
addition.calculate(10, 20);
System.out.println(result);
The lambda expression provides the implementation of the interface's single abstract method.
Built-in Functional Interfaces
Java provides commonly used functional interfaces in the
java.util.function package.
| Interface | Input | Output | Main Method |
|---|---|---|---|
Predicate<T> |
T | boolean | test() |
Function<T,R> |
T | R | apply() |
Consumer<T> |
T | void | accept() |
Supplier<T> |
None | T | get() |
UnaryOperator<T> |
T | T | apply() |
BinaryOperator<T> |
T, T | T | apply() |
Predicate<T>
Predicate represents a condition that returns either
true or false.
import java.util.function.Predicate;
Predicate<Integer> isPositive =
number -> number > 0;
System.out.println(
isPositive.test(10)
);
System.out.println(
isPositive.test(-5)
);
Function<T,R>
Function accepts one input and produces one output.
import java.util.function.Function;
Function<String, Integer> length =
text -> text.length();
int result =
length.apply("CIIT Institute");
System.out.println(result);
Consumer<T>
Consumer accepts an input but does not return a value.
import java.util.function.Consumer;
Consumer<String> printer =
text -> System.out.println(text);
printer.accept(
"Welcome to CIIT Institute"
);
Supplier<T>
Supplier produces a value without receiving an input.
import java.util.function.Supplier;
Supplier<String> message =
() -> "Hello Java";
System.out.println(
message.get()
);
UnaryOperator<T>
UnaryOperator represents a function where the input and output are of the same type.
import java.util.function.UnaryOperator;
UnaryOperator<Integer> square =
number -> number * number;
System.out.println(
square.apply(5)
);
BinaryOperator<T>
BinaryOperator accepts two values of the same type and returns a value of that same type.
import java.util.function.BinaryOperator;
BinaryOperator<Integer> addition =
(a, b) -> a + b;
System.out.println(
addition.apply(10, 20)
);
BiFunction<T,U,R>
BiFunction accepts two inputs and produces one output.
import java.util.function.BiFunction;
BiFunction<Integer, Integer, Integer> multiply =
(a, b) -> a * b;
System.out.println(
multiply.apply(5, 4)
);
BiPredicate<T,U>
BiPredicate accepts two inputs and returns a boolean result.
import java.util.function.BiPredicate;
BiPredicate<Integer, Integer> greater =
(a, b) -> a > b;
System.out.println(
greater.test(20, 10)
);
BiConsumer<T,U>
BiConsumer accepts two inputs and does not return a value.
import java.util.function.BiConsumer;
BiConsumer<String, Integer> display =
(name, age) ->
System.out.println(
name + " : " + age
);
display.accept(
"Amit",
22
);
Creating Your Own Functional Interface
@FunctionalInterface
interface MessageFormatter {
String format(String message);
}
MessageFormatter formatter =
message ->
"[INFO] " + message;
System.out.println(
formatter.format(
"Application started"
)
);
Functional Interface with Default Method
A functional interface may contain default methods because default methods are not abstract methods.
@FunctionalInterface
interface Greeting {
void sayHello();
default void welcome() {
System.out.println(
"Welcome!"
);
}
}
Functional Interface with Static Method
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
static int square(int number) {
return number * number;
}
}
Static methods belong to the interface itself and do not count as abstract methods.
Object Methods and Functional Interfaces
Methods that correspond to public methods of
Object do not count toward the single abstract
method requirement.
@FunctionalInterface
interface Task {
void execute();
String toString();
}
The interface still qualifies as functional because
toString() corresponds to a method from
Object.
Functional Interfaces and Method References
Method references can be assigned to functional interface variables when their signatures are compatible.
Consumer<String> printer =
System.out::println;
printer.accept(
"Hello from Java"
);
Functional Interfaces with Collections
List<String> names =
List.of(
"Amit",
"Neha",
"Rahul"
);
names.forEach(
name ->
System.out.println(name)
);
The forEach() operation accepts a
Consumer.
Functional Interfaces and Streams
Stream operations frequently accept functional interfaces.
List<Integer> numbers =
List.of(
10,
15,
20,
25,
30
);
numbers.stream()
.filter(
number -> number > 20
)
.map(
number -> number * 2
)
.forEach(
System.out::println
);
filter() uses a predicate-like condition,
map() transforms values, and
forEach() consumes values.
Composing Functional Operations
Some functional interfaces provide methods that allow operations to be combined.
Predicate<Integer> positive =
number -> number > 0;
Predicate<Integer> even =
number -> number % 2 == 0;
Predicate<Integer> positiveAndEven =
positive.and(even);
System.out.println(
positiveAndEven.test(10)
);
Function Composition
Function interfaces support operations such as
andThen() and compose().
Function<Integer, Integer> multiply =
number -> number * 2;
Function<Integer, Integer> add =
number -> number + 10;
Function<Integer, Integer> combined =
multiply.andThen(add);
System.out.println(
combined.apply(5)
);
Primitive Functional Interfaces
Java also provides primitive-specialized functional interfaces to reduce unnecessary boxing and unboxing in suitable cases.
| Interface | Purpose |
|---|---|
IntPredicate |
Predicate for int values. |
IntFunction<R> |
Accepts int and returns a reference type. |
IntConsumer |
Consumes an int value. |
IntSupplier |
Supplies an int value. |
IntUnaryOperator |
Transforms one int into another int. |
IntBinaryOperator |
Combines two int values. |
Example
Functional interfaces are useful when business logic needs to be supplied dynamically.
static int calculate(
int a,
int b,
BinaryOperator<Integer> operation
) {
return operation.apply(a, b);
}
int addition =
calculate(
10,
20,
(a, b) -> a + b
);
int multiplication =
calculate(
10,
20,
(a, b) -> a * b
);
System.out.println(addition);
System.out.println(multiplication);
Benefits of Functional Interfaces
- Enables lambda expressions.
- Reduces boilerplate code.
- Supports functional-style programming.
- Works naturally with the Stream API.
- Makes behavior easy to pass as an argument.
- Encourages reusable and composable operations.
Best Practices
-
Use
@FunctionalInterfacewhen defining an interface intended to be functional. -
Prefer standard interfaces from
java.util.functionwhen they fit the use case. - Create custom functional interfaces when domain-specific meaning improves readability.
- Keep lambda expressions short and readable.
- Prefer method references when they make the code clearer.
Interview Questions
@FunctionalInterface is an annotation
that documents and compiler-checks the intent that
an interface should contain one abstract method.
Predicate<T> represents a
function that accepts a value and returns a
boolean result.
Summary
Functional interfaces contain one abstract method and form the foundation for lambda expressions and method references. Java provides Predicate, Function, Consumer, Supplier, UnaryOperator, BinaryOperator, BiFunction, BiPredicate, BiConsumer, and primitive-specialized interfaces for common functional programming tasks.