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.