Home/Learn/Java A–Z/Functional Programming in Java

Functional Programming in Java

Intermediate
Modern Java

Java 8+ supports functional programming via lambdas, functional interfaces, and the java.util.function package — enabling composable, testable code.

Overview

Functional programming in Java centres on treating functions as first-class values: passing them as arguments, returning them, and composing them. The java.util.function package provides standard functional interfaces: Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>, BiFunction<T,U,R>, and more. Key concepts: pure functions (no side effects), immutability, function composition (compose, andThen), and currying/partial application.

Core Functional Interfaces

java.util.function provides the building blocks. Function<T,R>: takes T, returns R. Predicate<T>: takes T, returns boolean. Consumer<T>: takes T, returns nothing. Supplier<T>: takes nothing, returns T. Operator variants (UnaryOperator, BinaryOperator) for same input/output types.

FunctionalInterfaces.java
import java.util.function.*;

// Function<T, R> — transformation
Function<String, Integer> length = String::length;
Function<Integer, String> toStr  = Object::toString;

// compose: g.compose(f) = g(f(x))
Function<String, String> lengthStr = toStr.compose(length);
lengthStr.apply("hello"); // "5"

// andThen: f.andThen(g) = g(f(x))
Function<String, String> lengthStr2 = length.andThen(toStr);
lengthStr2.apply("hello"); // "5" (same result, different composition order)

// Predicate<T> — test
Predicate<String> isLong  = s -> s.length() > 5;
Predicate<String> isUpper = s -> s.equals(s.toUpperCase());
Predicate<String> isLongAndUpper = isLong.and(isUpper);
Predicate<String> either  = isLong.or(isUpper);
Predicate<String> notLong = isLong.negate();

// Consumer<T> — side effect
Consumer<String> print  = System.out::println;
Consumer<String> log    = s -> logger.info(s);
Consumer<String> printAndLog = print.andThen(log);

// Supplier<T> — lazy value
Supplier<List<String>> newList = ArrayList::new;
Supplier<Instant> now = Instant::now; // evaluated lazily

Higher-Order Functions and Currying

A higher-order function takes a function as a parameter or returns a function. This enables powerful abstractions: decorators, retry logic, caching wrappers, and pipeline builders.

Currying converts a multi-argument function into a chain of single-argument functions. Partial application fixes some arguments, returning a function that takes the rest.

HigherOrder.java
// Higher-order function — accepts a function
public static <T, R> List<R> map(List<T> list, Function<T, R> fn) {
    return list.stream().map(fn).collect(Collectors.toList());
}
List<Integer> lengths = map(List.of("a", "bb", "ccc"), String::length);

// Returns a function — timing decorator
public static <T, R> Function<T, R> timed(Function<T, R> fn, String name) {
    return input -> {
        long start = System.nanoTime();
        R result = fn.apply(input);
        long ms = (System.nanoTime() - start) / 1_000_000;
        System.out.println(name + " took " + ms + "ms");
        return result;
    };
}
Function<String, Integer> timedLength = timed(String::length, "length");

// Currying — Function<A, Function<B, C>>
Function<Integer, Function<Integer, Integer>> add =
    a -> b -> a + b;
Function<Integer, Integer> add5 = add.apply(5); // partial application
add5.apply(3); // 8
add5.apply(10); // 15

// Retry higher-order function
public static <T> Supplier<T> withRetry(Supplier<T> op, int maxAttempts) {
    return () -> {
        for (int i = 0; i < maxAttempts; i++) {
            try { return op.get(); }
            catch (Exception e) {
                if (i == maxAttempts - 1) throw e;
            }
        }
        throw new RuntimeException("unreachable");
    };
}

Pure Functions and Functional Style

A pure function always returns the same output for the same input and has no side effects. Pure functions are: easy to test (no mocking needed), safe to cache (memoization), and safe to run in parallel.

Functional style in Java: prefer immutable data, express transformations as stream pipelines, use Optional instead of null, and separate pure logic from I/O.

PureFunctions.java
// IMPURE — depends on external state, has side effects
private List<String> cache = new ArrayList<>();
public String processImpure(String input) {
    cache.add(input);          // side effect — modifies state
    return input + System.currentTimeMillis(); // non-deterministic
}

// PURE — same input always gives same output, no side effects
public static String processPure(String input, String suffix) {
    return input.strip().toLowerCase() + suffix;
}

// Memoization — safe because the function is pure
Map<String, Integer> memo = new ConcurrentHashMap<>();
Function<String, Integer> memoizedLength =
    s -> memo.computeIfAbsent(s, String::length);

// Functional pipeline — transformations as a pipeline of pure functions
List<String> result = rawData.stream()
    .filter(s -> !s.isBlank())            // pure predicate
    .map(String::trim)                    // pure transform
    .map(String::toLowerCase)             // pure transform
    .distinct()                           // stateful but within stream
    .sorted()                             // pure compare
    .collect(Collectors.toUnmodifiableList()); // immutable result

Key Points to Remember

  • Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T> are the core functional interfaces.
  • compose() applies right-to-left; andThen() applies left-to-right.
  • Higher-order functions take or return functions — enables decorators, retry, timing wrappers.
  • Currying/partial application: fix some arguments, return a function for the rest.
  • Pure functions (no side effects, deterministic) are easy to test, cache, and parallelise.

Practice Functional Programming in Java in the Playground

Run and modify code directly in your browser - no setup needed.

Interview Questions

Sign in to ask Aria
1

What is the difference between Function.compose() and Function.andThen()?

MediumGoogle
2

What is a pure function and why is it beneficial?

EasyAmazon
3

What is currying and how do you implement it in Java?

HardOracle
4

What is the difference between Consumer and Function?

EasyMicrosoft
5

How would you implement a memoization wrapper as a higher-order function?

HardNetflix

Ask Aria about Functional Programming in Java

Your personal AI tutor — ask anything about this concept