- Programming paradigm where computation is expressed as evaluation of functions.
- Focuses on what to do rather than how to do it.
- Encourages immutability, statelessness, and function composition.
- Java supports hybrid FP (not purely functional).
A Functional Interface = interface with exactly 1 abstract method (SAM – Single Abstract Method).
- Used in Lambdas and Method References.
- Can also have default and static methods.
@FunctionalInterface
interface Calculator {
int operate(int a, int b);
}| Interface | Method Signature | Example |
|---|---|---|
Consumer<T> |
void accept(T t) |
Consumer<String> printer = s -> System.out.println(s); |
BiConsumer<T,U> |
void accept(T t, U u) |
BiConsumer<String,Integer> disp = (n,a)->System.out.println(n+":"+a); |
IntConsumer |
void accept(int v) |
IntConsumer ic = x -> System.out.println(x*2); |
| Interface | Method Signature | Example |
|---|---|---|
Supplier<T> |
T get() |
Supplier<Double> rand = () -> Math.random(); |
| Interface | Method Signature | Example |
|---|---|---|
Function<T,R> |
R apply(T t) |
Function<String,Integer> len = s -> s.length(); |
BiFunction<T,U,R> |
R apply(T t, U u) |
BiFunction<Integer,Integer,Integer> add = (a,b)->a+b; |
UnaryOperator<T> |
T apply(T t) |
UnaryOperator<Integer> sq = x -> x*x; |
BinaryOperator<T> |
T apply(T t, T u) |
BinaryOperator<Integer> max = Integer::max; |
| Interface | Method Signature | Example |
|---|---|---|
Predicate<T> |
boolean test(T t) |
Predicate<Integer> even = n -> n%2==0; |
BiPredicate<T,U> |
boolean test(T t,U u) |
BiPredicate<String,Integer> longer = (s,n)->s.length()>n; |
Shorthand for lambdas that just call methods.
- Static →
ClassName::staticMethod
Function<String,Integer> parser = Integer::parseInt;- Instance (particular object) →
instance::method
Consumer<String> printer = System.out::println;- Instance (arbitrary object of a type) →
ClassName::method
Function<String,String> upper = String::toUpperCase;- Constructor →
ClassName::new
Supplier<List<String>> listSupplier = ArrayList::new;Streams use functional interfaces:
filter(Predicate<T>)map(Function<T,R>)forEach(Consumer<T>)reduce(BinaryOperator<T>)
List<String> names = Arrays.asList("John", "Maya", "Hardik");
names.stream()
.filter(s -> s.startsWith("H")) // Predicate
.map(String::toUpperCase) // Function
.forEach(System.out::println); // ConsumerOptional<String> name = Optional.ofNullable("Hardik");
name.ifPresent(System.out::println); // Consumer
String result = name.orElseGet(() -> "Default"); // Supplier- ✅ Concise, expressive, less boilerplate.
- ✅ Encourages immutability → fewer bugs.
- ✅ Easy parallelism with Streams.
- ✅ Declarative style (focus on what, not how).
- ✅ Functions are composable & testable.
Use When:
- Data processing pipelines (filter, map, reduce).
- Callbacks, event handling.
- Concurrency with
CompletableFuture,parallelStream.
Avoid When:
- Performance-critical loops (lambda overhead).
- Heavy mutable state.
- Complex recursion (no tail-call optimization).
- Collections (List, Set, Map) → perfect with Streams.
- Concurrent Collections (e.g., ConcurrentHashMap) with
computeIfAbsent,merge. - Immutable Collections (Java 9+:
List.of(),Map.of()). - Arrays with
Arrays.stream(). - Optional for null-safety.
import java.util.*;
import java.util.function.*;
public class FunctionalDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Maya", "Hardik", "Meera");
Predicate<String> startsWithM = s -> s.startsWith("M");
Function<String, Integer> length = String::length;
Consumer<String> printer = System.out::println;
Supplier<Date> now = Date::new;
names.stream()
.filter(startsWithM) // Predicate
.map(length.andThen(Object::toString)) // Function
.forEach(printer); // Consumer
System.out.println("Current Date: " + now.get());
}
}✅ This cheat sheet covers all major functional interfaces, method references, usage in Streams/Optional, advantages, best practices, and examples.