Lambda is a short form of anonymous function. This is a core grammar that can be found almost everywhere since JDK 8, including collections, streams, and spring. Also learn the method references and standard functional interfaces (Function/Predicate/Consumer/Supplier).
Learning Objectives
- Know the form of lambda expression (
(a, b) -> a + b) - Know 4 method references (
Foo::bar) - Uses the core 4 interfaces of
java.util.function - Know the constraints (effective final) of variables that a lambda can capture
Key concepts
1) lambda expression
Runnable r = () -> System.out.println("hi");
Comparator<String> byLen = (a, b) -> a.length() - b.length();
The type is inferred from the target functional interface.
2) 4 method references
| Type | Yes |
|---|---|
| static method | Integer::parseInt |
| instance method (specific object) | System.out::println |
| instance method (class name) | String::toUpperCase |
| constructor | ArrayList::new |
3) Standard functional interface
| interface | abstract method | Meaning |
|---|---|---|
Function<T, R> |
R apply(T) |
T → R conversion |
Predicate<T> |
boolean test(T) |
T condition check |
Consumer<T> |
void accept(T) |
T consumption (power, etc.) |
Supplier<T> |
T get() |
T supplied without parameters |
BiFunction<T,U,R> |
R apply(T, U) |
Two inputs → one output |
4) effective final
int x = 10;
Runnable r = () -> System.out.println(x);
//x = 20; // Compilation error: Captured variable must be final in nature
Local variables captured inside a lambda (even without the final keyword) must be immutable once set.
See as example
Example 1 — LambdaBasics.java: Various Forms
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class LambdaBasics {
public static void main(String[] args) {
Runnable r = () -> System.out.println("hi");
r.run();
Comparator<String> byLen = (a, b) -> a.length() - b.length();
List<String> words = Arrays.asList("ccc", "a", "bb");
words.sort(byLen);
System.out.println(words);
}
}
Execution result
hi
[a, bb, ccc]
Note: If the lambda body is one line, you can omit return and the curly braces.
Example 2 — MethodReference.java: Method reference
import java.util.List;
import java.util.stream.Collectors;
public class MethodReference {
public static void main(String[] args) {
List<String> langs = List.of("java", "kotlin", "scala");
// (s) -> s.toUpperCase()
List<String> upper = langs.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upper);
// (s) -> System.out.println(s)
langs.forEach(System.out::println);
}
}
Execution result
[JAVA, KOTLIN, SCALA]
java
kotlin
scala
Note: If the lambda simply "calls some method", the method reference is shorter and clearer.
Example 3 — FunctionInterfaces.java: 4 standard types
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunctionInterfaces {
public static void main(String[] args) {
Function<Integer, Integer> square = n -> n * n;
Predicate<Integer> isEven = n -> n % 2 == 0;
Consumer<String> printUp = s -> System.out.println(s.toUpperCase());
Supplier<String> hello = () -> "Hello";
System.out.println(square.apply(5));
System.out.println(isEven.test(10));
printUp.accept("java");
System.out.println(hello.get());
}
}
Execution result
25
true
JAVA
Hello
Memo: The interface of a lambda is naturally determined by looking at what it takes and what it returns.
Example 4 — Capture.java: Capturing variables
public class Capture {
public static void main(String[] args) {
int x = 10;
Runnable r = () -> System.out.println("x=" + x);
r.run();
//x = 20; // Compilation error if uncommented
for (int i = 0; i < 3; i++) {
final int idx = i;
Runnable rr = () -> System.out.println("idx=" + idx);
rr.run();
}
}
}
Execution result
x=10
idx=0
idx=1
idx=2
Note: i of for itself is created anew each time, but to capture it inside a lambda, you need to make a final copy of that round.
Common Mistakes
- Forcibly write
returnin a single line lambda (does not need to be present) - Reassignment to capture variable → compilation error
- Forgetting exception handling in lambda and conflicting with
Functionsignature (checked exception) - Confusion about whether
String::toUpperCaseis an instance method or a static method. - Long use of lambda even when method reference is available
Cleanup
- Lambda is a short implementation of a functional interface.
- More concise with method references
- 80% utilization possible by learning only the standard 4 functional interfaces
- Capture variables must be effective final
Try it yourself
cd 05_Modern_Java/18_Lambda-and_functional_type/src
javac LambdaBasics.java
java LambdaBasics