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.