Lab: turning a long lambda into a method reference¶
☕ Java 17 · How Lambdas See the Variables Around Them · 4/4
Goal: refactor a filter buried in a block lambda into a named method, then a method reference, and prove all versions behave the same.
Setup: create Movie.java and CatalogFilter.java in the same folder, with no package.
Step 1 — The data¶
Step 2 — The "before" version¶
Start CatalogFilter.java:
import java.util.function.Predicate;
public class CatalogFilter {
// Before: logic buried in a lambda
Predicate<Movie> hit = m -> {
int minMinutes = 90;
double minRating = 4.0;
return m.minutes() >= minMinutes
&& m.rating() > minRating;
};
Braces, two locals and a return: a warning sign. The reader must work through every line to learn what's checked, and no name says "feature hit".
Try adding int m = 0; under minRating: the compiler answers variable m is already defined, because the body shares the scope of its parameter. Delete that line.
Step 3 — Name it, then reference it¶
// Step 1: give the logic a name
boolean isFeatureHit(Movie m) {
return m.minutes() >= 90 && m.rating() > 4.0;
}
// Step 2: a one-line lambda...
Predicate<Movie> p1 = m -> isFeatureHit(m);
// Step 3: ...or a method reference
Predicate<Movie> p2 = this::isFeatureHit;
isFeatureHitstates the intent, is reusable and testable, and itsmlives in its own method scope.p1only forwards its parameter, so it can becomethis::isFeatureHit: it fitsPredicate<Movie>(takes aMovie, returns aboolean).
Step 4 — Prove they agree¶
public static void main(String[] args) {
CatalogFilter filter = new CatalogFilter(); // this::isFeatureHit needs a real object
Movie[] catalog = {
new Movie("Inception", 148, 4.8),
new Movie("Night Drive", 95, 4.1),
new Movie("Coffee Break Shorts", 22, 4.5),
new Movie("The Endless Saga", 160, 3.2),
new Movie("Ninety Minutes Flat", 90, 4.0)
};
for (Movie movie : catalog) {
System.out.println(movie.title()
+ " -> before: " + filter.hit.test(movie)
+ " | lambda: " + filter.p1.test(movie)
+ " | method ref: " + filter.p2.test(movie));
}
}
}
Predict first: Ninety Minutes Flat passes on minutes (>=) but fails on rating (> 4.0, not >=).
$ javac Movie.java CatalogFilter.java && java CatalogFilter
Inception -> before: true | lambda: true | method ref: true
Night Drive -> before: true | lambda: true | method ref: true
Coffee Break Shorts -> before: false | lambda: false | method ref: false
The Endless Saga -> before: false | lambda: false | method ref: false
Ninety Minutes Flat -> before: false | lambda: false | method ref: false
Every row agrees. Same behavior, very different readability: this::isFeatureHit tells the whole story in one line. Refactoring changes how clearly code says something, never what it does.
← The effectively final rule · Back to the series
🎓 Preparing the OCP Java 17 exam? Exam traps and practice questions for this section → (optional)