A lambda is an anonymous function you can pass around as a value; a method reference is a lambda that just calls an existing method.
Syntax forms
| Form | Example |
|---|---|
| No parameters | () -> 42 |
| One parameter (parentheses optional) | s -> s.length() |
| Several parameters | (a, b) -> a + b |
| Explicit types | (String a, String b) -> a.compareTo(b) |
var parameters (11+, allows annotations) | (var a, var b) -> a + b |
Block body (needs return) | x -> { int y = x * 2; return y + 1; } |
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of("Ravi", "Asha", "Zoe", "Mohan"));
// Before Java 8: an anonymous class
names.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
System.out.println(names);
// Java 8+: a lambda
names.sort((a, b) -> b.length() - a.length());
System.out.println(names);
names.removeIf(s -> s.length() < 4);
names.forEach(s -> System.out.print(s + " "));
System.out.println();
}
}[Asha, Mohan, Ravi, Zoe]
[Mohan, Asha, Ravi, Zoe]
Mohan Asha RaviCapturing variables: effectively final
A lambda can read local variables from the enclosing method only if they are effectively final — never reassigned after initialisation. The lambda captures the value, and Java forbids the variable changing underneath it.
public class Main {
public static void main(String[] args) {
int count = 0;
Runnable r = () -> count++; // error: must be final or effectively final
r.run();
}
}import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) {
String prefix = "item-"; // effectively final: OK to capture
AtomicInteger counter = new AtomicInteger(); // the reference is final; its state can change
List.of("a", "b", "c").forEach(s -> {
counter.incrementAndGet();
System.out.println(prefix + s);
});
System.out.println("count = " + counter.get());
}
}item-a
item-b
item-c
count = 3this in a lambda vs an anonymous class
public class Main {
String name = "outer";
void demo() {
Runnable lambda = () -> System.out.println("lambda this: " + this.name);
Runnable anon = new Runnable() {
String name = "anonymous";
@Override
public void run() {
System.out.println("anon this: " + this.name);
}
};
lambda.run();
anon.run();
}
public static void main(String[] args) {
new Main().demo();
}
}lambda this: outer
anon this: anonymous| Lambda | Anonymous class | |
|---|---|---|
this refers to | the enclosing instance | the anonymous object itself |
| New scope? | No — can't redeclare an enclosing local name | Yes — can shadow names |
| Implements | only functional interfaces | any interface or class, many methods, can hold state |
| Compiled to | invokedynamic + a private method (no extra .class file) | a separate Outer$1.class |
Method references — four kinds
When a lambda only forwards its arguments to one method, write Class::method instead.
| Kind | Syntax | Equivalent lambda |
|---|---|---|
| Static method | Integer::parseInt | s -> Integer.parseInt(s) |
| Instance method of a particular object | System.out::println | x -> System.out.println(x) |
| Instance method of an arbitrary object of a type | String::toUpperCase | s -> s.toUpperCase() |
| Constructor | ArrayList::new | () -> new ArrayList<>() |
import java.util.*;
import java.util.function.*;
public class Main {
public static void main(String[] args) {
Function<String, Integer> parse = Integer::parseInt; // static
String greeting = "Hello";
Predicate<String> startsWithIt = greeting::startsWith; // particular object
Function<String, String> upper = String::toUpperCase; // arbitrary object
BiFunction<String, String, Boolean> eq = String::equalsIgnoreCase; // first arg is the receiver
Supplier<List<String>> newList = ArrayList::new; // constructor
Function<Integer, int[]> newArray = int[]::new; // array constructor
System.out.println(parse.apply("41") + 1);
System.out.println(startsWithIt.test("Hell"));
System.out.println(upper.apply("java"));
System.out.println(eq.apply("JAVA", "java"));
List<String> list = newList.get();
list.add("x");
System.out.println(list + " " + newArray.apply(3).length);
}
}42
true
JAVA
true
[x] 3Are lambdas just syntactic sugar for anonymous classes?
Why must captured local variables be effectively final?
What does String::compareTo mean when a Comparator<String> is expected?