Lambda-Funktionen und funktionaler Stil in Java: eine ausführliche Anleitung
What is it and why? (a real-life analogy)
Imagine you are a head chef in a restaurant. You have junior cooks who can execute simple commands: "peel the potato", "chop the onion", "wash the dishes". Each cook is a method (function). Everything is clear and familiar.
But suddenly an important guest arrives and says: "I need a special dish. I want you to invent a new way of cutting vegetables right now — spiralized — and apply it only to my salad." You cannot train all the cooks in advance for this — the method is needed once, right now.
This is where lambda functions come to the rescue. It's like writing an instruction on a napkin: "Take a carrot and cut it into a spiral, like this..." — and handing it to the cook. He reads it, does it, and throws the napkin away. You didn't need to create a new cook with a name, you didn't need to write this method in the recipe book. You simply passed behavior (an action) as an object.
The essence of lambda: it is an anonymous (nameless) function that can be created on the fly and passed to another method or stored in a variable. It makes code shorter, cleaner, and more expressive.
Why is this needed in real programming? Imagine you have a list of numbers, and you need to:
- filter only even numbers;
- multiply each number by 2;
- sort in descending order;
- print the result.
Without lambdas, you would have to write separate classes with methods, create anonymous inner classes (which is cumbersome). With lambdas, this is done in 3-4 lines. Lambdas are a step towards the functional style, where we think not "how to do it", but "what to do".
Theory: how it works internally
To understand lambdas, you need to grasp the key concept — the functional interface. This is an interface that has exactly one abstract method. For example, Runnable (method run()), Comparator (method compare()), ActionListener (method actionPerformed()).
A lambda expression is simply a concise form of implementing that single method. The Java compiler looks at the context: if a functional interface is expected, it understands that the lambda is its implementation.
Internally, Java turns the lambda into a special object. But it does this cleverly: it does not create a separate class file for each lambda (as it does for anonymous classes). Instead, it uses invokedynamic — a bytecode instruction that creates the implementation on the fly, efficiently and quickly. This is one of the reasons lambdas work faster than anonymous classes.
A lambda expression consists of three parts:
- Parameter list in parentheses:
(int x, int y) - Arrow:
-> - Body: an expression or a block of code in curly braces
If the body is a single expression, curly braces and return are not needed. If the body is a block, both braces and return are needed (if the method returns a value).
Important: a lambda does not have its own name, does not belong to a class, and cannot have its own fields. It simply implements the method of a functional interface.
Syntax in Java: all forms and variants
Let's look at all possible forms of writing lambda expressions using the functional interface Comparator<Integer> (method int compare(Integer a, Integer b)) as an example.
| Form | Example | When to use |
|---|---|---|
| Full (with types) | (Integer a, Integer b) -> { return a - b; } |
When you need to explicitly specify types (rare) |
| With type inference | (a, b) -> { return a - b; } |
Standard case, types are taken from the context |
| Single expression (without return) | (a, b) -> a - b |
When the body is a single expression (most common case) |
| Single parameter (without parentheses) | a -> a * 2 |
When there is exactly one parameter, parentheses can be omitted |
| No parameters | () -> System.out.println("Hello") |
For interfaces like Runnable |
| Method reference | Integer::compare |
When the lambda simply calls an existing method |
Method references are an even more concise form. If your lambda looks like (x) -> SomeClass.someMethod(x), it can be replaced with SomeClass::someMethod. There are four types of method references:
| Type | Syntax | Example |
|---|---|---|
| Static method | ClassName::staticMethod |
Integer::parseInt |
| Instance method of a specific object | object::instanceMethod |
list::add |
| Instance method of an arbitrary object | ClassName::instanceMethod |
String::length |
| Constructor | ClassName::new |