Combining filters and chaining functions¶
โ Java 17 ยท The Ready-Made Lambda Toolkit ยท 2/4
The built-in interfaces have a single abstract method, but also default methods that build new lambdas from existing ones.
Combining predicates¶
Writing every combination by hand duplicates logic:
Predicate<Movie> kidsComedy = m -> m.isKids() && m.genre() == COMEDY;
Predicate<Movie> adultComedy = m -> !m.isKids() && m.genre() == COMEDY;
Build small rules once, then combine them:
Predicate<Movie> kids = Movie::isKids;
Predicate<Movie> comedy = m -> m.genre() == COMEDY;
var kidsComedy = kids.and(comedy); // both true
var adultComedy = kids.negate().and(comedy); // not kids, and comedy
var either = kids.or(comedy); // at least one true
| Method | Result |
|---|---|
p.and(q) |
true when both are true |
p.or(q) |
true when at least one is true |
p.negate() |
the opposite of p |
Each returns a brand-new Predicate. If the definition of a comedy changes, you edit comedy once and every combination follows.
Chaining consumers¶
Consumer<String> log = t -> System.out.print("Log: " + t);
Consumer<String> push = t -> System.out.print(" | Push: " + t);
log.andThen(push).accept("Ozark"); // Log: Ozark | Push: Ozark
andThen() runs both consumers in order, giving both the same input. Nothing is passed from one to the other.
andThen() versus compose()¶
Functions pass their result along, and the order matters:
Function<Integer, Integer> addAds = min -> min + 5;
Function<Integer, Integer> toSeconds = min -> min * 60;
addAds.andThen(toSeconds).apply(45); // (45 + 5) * 60 = 3000
addAds.compose(toSeconds).apply(45); // (45 * 60) + 5 = 2705
| Call | Runs first | Then |
|---|---|---|
f.andThen(g) |
f |
g receives f's result |
f.compose(g) |
g |
f receives g's result |
Read andThen left to right; read compose right to left. Same pieces, different order, different answer: compute both on paper when in doubt.
Every interface so far works with objects. For int, long, double and boolean, Java has lighter versions.