7-day free trial — no payment today.

Lambdas & Functional Interfaces in Java

Write concise behaviour with lambda expressions, method references, and built-in functional interfaces from java.util.function (Function, Predicate, Consumer, Supplier).

Lambda Expressions

A lambda expression is an anonymous function — a block of code you can pass around like a value. Before lambdas, you had to create anonymous inner classes to pass behaviour. Lambdas replaced that boilerplate with a clean `(parameters) -> body` syntax. They are the foundation of functional-style programming in Java.

1. Lambda Syntax

The lambda syntax is `(parameters) -> expression` or `(parameters) -> { statements; }`. Parameter types can be omitted — Java infers them.

2. Lambdas with Parameters and Return Values

Lambdas can take parameters and return values. For a single expression, the return is implicit — no `return` keyword needed.

3. Capturing Variables

Lambdas can read variables from the enclosing scope, but those variables must be effectively final — not modified after assignment.

Functional Interfaces

A functional interface is an interface with exactly one abstract method. This is what makes lambdas possible — the lambda provides the implementation of that single method. Java's `@FunctionalInterface` annotation enforces this contract at compile time. Every lambda in Java targets a functional interface.

1. What Makes an Interface Functional

An interface with exactly one abstract method is a functional interface. It can have default and static methods — they do not count.

2. Custom Functional Interface with Generic Type

Make your functional interface generic so it can work with any type — just like the built-in ones in `java.util.function`.

Built-in Functional Interfaces

Java provides a full set of ready-to-use functional interfaces in `java.util.function`. You rarely need to define your own. The four core ones are `Function<T,R>` (transform), `Predicate<T>` (test), `Consumer<T>` (consume), and `Supplier<T>` (produce). Each has variants for two inputs, primitive types, and chaining.

1. Function<T, R>

`Function<T, R>` takes one argument of type T and returns a result of type R. Use `andThen` and `compose` to chain functions.

2. Predicate<T>

`Predicate<T>` tests a condition and returns boolean. Combine predicates with `and`, `or`, and `negate`.

3. Consumer<T>

`Consumer<T>` takes one argument, performs an action, and returns nothing. Use `andThen` to chain multiple consumers.

4. Supplier<T>

`Supplier<T>` takes no arguments and produces a value. Useful for lazy evaluation, factory methods, and default value generation.

5. UnaryOperator and BinaryOperator

`UnaryOperator<T>` is a Function where input and output are the same type. `BinaryOperator<T>` is a BiFunction where both inputs and the output are the same type.

Method References

A method reference is a shorthand for a lambda that does nothing but call an existing method. Instead of `s -> s.toUpperCase()`, you write `String::toUpperCase`. There are four kinds: static method, instance method on a specific object, instance method on an arbitrary object of a type, and constructor reference.

1. Static Method Reference

`ClassName::staticMethod` replaces a lambda that calls a static method.

2. Instance Method Reference (Specific Object)

`object::instanceMethod` — the lambda always calls the method on that one specific object.

3. Instance Method Reference (Arbitrary Instance of a Type)

`ClassName::instanceMethod` — the first argument of the lambda becomes the receiver object.

4. Constructor Reference

`ClassName::new` creates a method reference to a constructor — useful as a factory Supplier or Function.

Composing and Combining Functions

One of the biggest strengths of functional interfaces is composability — you can combine small, focused functions into larger pipelines. Java's built-in functional interfaces all provide composition methods so you can build complex logic from simple pieces without writing new classes.

1. Building a Processing Pipeline

Chain multiple `Function`s with `andThen` to form a clean data processing pipeline.

2. Combining Predicates for Complex Filters

Combine multiple `Predicate`s with `and`, `or`, and `negate` to build readable multi-condition filters.

3. Real-World Functional Pipeline

Combine Function, Predicate, Consumer, and Supplier together in a realistic data-processing scenario.