Introduction
In the last episode, BiPredicate evaluated two values together and returned a verdict.
Now, in our final stop, let's meet BiConsumer: it takes two inputs and performs an action, no result, just effect.
If Consumer acts on one value, BiConsumer acts on a pair.
What Is BiConsumer?
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after);
return (t, u) -> {
accept(t, u);
after.accept(t, u);
};
}
}
Input: two objects, of types T and U.
Output: none (void).
Purpose: perform an action that depends on two values at once, typically with side effects.
Why Use BiConsumer?
Before, iterating a map and doing something with both key and value meant a hand-rolled loop:
for (Map.Entry<String, Integer> entry : stock.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
BiConsumer turns that action into a first-class, reusable value, and Map.forEach accepts it directly:
BiConsumer<String, Integer> printStock = (item, qty) -> System.out.println(item + ": " + qty);
stock.forEach(printStock);
Practical Examples
1. Iterating a Map
Map<String, Integer> stock = Map.of("apples", 10, "bananas", 4);
stock.forEach((item, qty) -> System.out.println(item + ": " + qty));
2. Logging Pairs
BiConsumer<String, Integer> logLowStock = (item, qty) -> {
if (qty < 5) System.out.println("Low stock: " + item);
};
stock.forEach(logLowStock);
3. Chaining with andThen
BiConsumer<String, Integer> print = (item, qty) -> System.out.println(item + ": " + qty);
BiConsumer<String, Integer> alert = (item, qty) -> {
if (qty < 5) System.out.println("Alert! " + item + " is low");
};
BiConsumer<String, Integer> pipeline = print.andThen(alert);
pipeline.accept("bananas", 4);
// bananas: 4
// Alert! bananas is low
4. Event Handling with Two Parameters
BiConsumer<Order, String> onStatusChange = (order, newStatus) ->
System.out.println("Order " + order.getId() + " -> " + newStatus);
onStatusChange.accept(currentOrder, "SHIPPED");
Real-World Patterns
-
Map Iteration:
forEachover aMapis the most natural home forBiConsumer. - Contextual Callbacks: Passing both an event and its context to a handler in one call.
- Coordinated Logging / Auditing: Recording two related pieces of information together, e.g. an entity and the action performed on it.
- Two-Argument Event Handlers: Modeling listeners that need more than a single piece of data to act.
Best Practices
-
Isolate Side Effects: Keep a
BiConsumerfocused on one observable effect at a time. -
Name Both Arguments' Role:
onStatusChange,logLowStockcommunicate more than a generic handler. -
Compose Carefully: Use
andThenfor a short, clear sequence of effects, not a long hidden chain.
Common Pitfalls
-
Thread Safety in Parallel forEach: A
BiConsumerthat mutates shared state can race when used with a parallel stream or a concurrent map. -
Overuse for Transformation: If the goal is to produce a result, reach for
BiFunction, don't smuggle a return value out through a captured variable. -
Traceability: Multiple chained
BiConsumers with side effects can make it hard to know, at a glance, everything a singleaccept()call actually does.
Functional Analogy
Think of BiConsumer as a two-person delivery:
- Two packages arrive together, T and U.
- Both get handled in the same visit.
- Nothing is handed back, the effect is what mattered.
Conclusion
BiConsumer closes the loop on Java's core functional interfaces. Across ten episodes, we've covered every shape a single or paired value can take: a decision (Predicate, BiPredicate), a transformation (Function, BiFunction, UnaryOperator, BinaryOperator), an action (Consumer, BiConsumer), and a source (Supplier). Together, they form a small but complete vocabulary for functional Java.
What's Next
With all nine core interfaces covered, the next natural step is putting them to work together, composing predicates, functions, and consumers into a single real-world pipeline. That's where we're headed next: turning vocabulary into fluency. 🚀
Top comments (0)