Functional Interfaces
1. What Is a Functional Interface?
A functional interface has exactly one abstract method and can be implemented using a lambda.
@FunctionalInterface
interface Calculator {
int add(int a, int b);
}
Calculator c = (a, b) -> a + b;
System.out.println(c.add(10, 20)); // 30
Java provides many functional interfaces in java.util.function.
2. Four Main Functional Interfaces
Supplier<T>
Gives/produces a value.
Input → Nothing
Output → Value
Method → get()
Supplier<String> s = () -> "Hello";
System.out.println(s.get()); // Hello
Think: Supplier → gives something
Consumer<T>
Takes a value and does something, returns nothing.
Input → Value
Output → Nothing
Method → accept(T)
Consumer<String> print = x -> System.out.println(x);
print.accept("Hello");
Common use:
names.forEach(name -> System.out.println(name));
Think: Consumer → uses something
Predicate<T>
Checks a condition and returns true or false.
Input → Value
Output → boolean
Method → test(T)
Predicate<String> isShort = s -> s.length() < 5;
System.out.println(isShort.test("cat")); // true
System.out.println(isShort.test("hello")); // false
Think: Predicate → asks a question
Function<T,R>
Converts one value into another.
Input → T
Output → R
Method → apply(T)
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Hello")); // 5
Here:
String → Integer
Think: Function → transforms something
3. Easy Way to Remember
| Interface | Takes | Returns | Think |
|---|---|---|---|
Supplier<T> |
Nothing | T |
Give |
Consumer<T> |
T |
Nothing | Use |
Predicate<T> |
T |
boolean |
Check |
Function<T,R> |
T |
R |
Transform |
Supplier : () → value
Consumer : value → void
Predicate : value → true/false
Function : value → another value
4. Bi Interfaces
Bi versions simply take two inputs.
BiConsumer<T,U>
BiConsumer<String, Integer> print =
(name, age) -> System.out.println(name + " is " + age);
print.accept("Alice", 22);
2 inputs → nothing
BiPredicate<T,U>
BiPredicate<Integer, Integer> greater =
(a, b) -> a > b;
System.out.println(greater.test(10, 5)); // true
2 inputs → boolean
BiFunction<T,U,R>
BiFunction<Integer, Integer, Integer> add =
(a, b) -> a + b;
System.out.println(add.apply(10, 20)); // 30
2 inputs → result
5. Primitive Specialized Interfaces
Generic types such as:
Function<Integer, Integer>
use wrapper objects (Integer) and may involve boxing/unboxing.
Java provides primitive versions:
IntSupplier
IntConsumer
IntPredicate
IntFunction<R>
Example:
IntPredicate isEven = n -> n % 2 == 0;
System.out.println(isEven.test(10)); // true
Use IntPredicate when working directly with int.
6. UnaryOperator<T>
UnaryOperator<T> is basically:
Function<T,T>
It takes and returns the same type.
UnaryOperator<Integer> doubleNumber = n -> n * 2;
System.out.println(doubleNumber.apply(5)); // 10
Integer → Integer
7. Functional Interfaces with Collections
forEach() → Consumer
names.forEach(name -> System.out.println(name));
It takes each element and does something with it.
removeIf() → Predicate
numbers.removeIf(n -> n % 2 == 0);
The predicate returns:
true → remove element
false → keep element
8. Functional Interface + Lambda
The relationship is:
Functional Interface
↓
One abstract method
↓
Lambda implements it
Example:
Predicate<String> p = s -> s.length() == 3;
Predicate defines:
boolean test(String s);
The lambda provides its implementation:
s -> s.length() == 3
Then:
p.test("cat"); // true
p.test("hello"); // false
Quick Revision
| Interface | Method | Purpose |
|---|---|---|
Supplier<T> |
get() |
Produces a value |
Consumer<T> |
accept(T) |
Uses a value |
Predicate<T> |
test(T) |
Checks a condition |
Function<T,R> |
apply(T) |
Converts a value |
BiConsumer<T,U> |
accept(T,U) |
Uses 2 values |
BiPredicate<T,U> |
test(T,U) |
Tests 2 values |
BiFunction<T,U,R> |
apply(T,U) |
Converts 2 values |
UnaryOperator<T> |
apply(T) |
T → T |
Remember these four
Supplier → gives
Consumer → uses
Predicate → checks
Function → transforms
Once these four are clear, most of java.util.function becomes easy to understand.