Java Consumer Interface
Last modified: April 16, 2025
The java.util.function.Consumer interface represents an operation
that accepts a single input argument and returns no result. It is a functional
interface with a single abstract method accept. Consumer is used
for side-effect operations like printing or modifying objects.
Consumer is part of Java's functional programming utilities added
in Java 8. Unlike most functional interfaces, Consumer operations are meant to
produce side effects. The interface provides default methods for consumer
chaining.
Consumer Interface Overview
Consumer interface contains one abstract method and one default
method. The key method accept performs the operation on the input.
The andThen method enables consumer chaining.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after);
}
The code above shows the structure of Consumer interface. It uses
generics where T is the input type. The interface is annotated with
@FunctionalInterface to indicate its single abstract method nature.
Basic Consumer Usage
The simplest way to use Consumer is with lambda expressions. We define what to do with the input in the accept method. The example prints strings to console.
package com.zetcode;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
// Define a consumer that prints strings
Consumer<String> printer = s -> System.out.println(s);
// Use the consumer
printer.accept("Hello, Consumer!");
// Consumer using method reference
Consumer<String> methodRefPrinter = System.out::println;
methodRefPrinter.accept("Method reference consumer");
}
}
This example demonstrates basic Consumer usage with lambda and method reference. The printer consumer takes a String and prints it. Method reference provides more concise syntax for existing methods that match Consumer's signature.
Consumer with Collections
Consumer is commonly used with collections through the forEach
method. This enables clean iteration patterns without explicit loops. The
example processes a list of numbers.
package com.zetcode;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Consumer to print numbers with prefix
Consumer<Integer> numberPrinter = n ->
System.out.println("Number: " + n);
// Apply consumer to each element
numbers.forEach(numberPrinter);
// Inline consumer
numbers.forEach(n -> System.out.println(n * 2));
}
}
This example shows Consumer usage with collections. We define a number printer consumer and pass it to forEach. The second forEach demonstrates an inline consumer. Both approaches process each collection element.
Consumer Chaining with andThen
The andThen method allows chaining consumers where each consumer
processes the same input in sequence. This enables creating complex processing
pipelines from simple consumers.
package com.zetcode;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
// First consumer logs the message
Consumer<String> logger = s ->
System.out.println("LOG: " + s);
// Second consumer sends email notification
Consumer<String> notifier = s ->
System.out.println("Sending notification: " + s);
// Chain the consumers
Consumer<String> processor = logger.andThen(notifier);
// Process a message
processor.accept("System started successfully");
}
}
This example shows consumer chaining with andThen. The input
message first gets logged, then triggers a notification. Both consumers
receive the same input. The order of execution is guaranteed.
Modifying Objects with Consumer
Consumers are often used to modify object state. Since they accept arguments but return nothing, they're perfect for mutating operations. The example updates product prices.
package com.zetcode;
import java.util.function.Consumer;
class Product {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return name + ": $" + price;
}
}
public class Main {
public static void main(String[] args) {
Product laptop = new Product("Laptop", 999.99);
// Consumer to apply discount
Consumer<Product> discount = p ->
p.price = p.price * 0.9; // 10% discount
System.out.println("Before: " + laptop);
discount.accept(laptop);
System.out.println("After: " + laptop);
}
}
This example demonstrates object modification with Consumer. The discount consumer takes a Product and reduces its price by 10%. Consumers are ideal for such state-changing operations while keeping code clean.
BiConsumer Interface
While Consumer takes one argument, BiConsumer handles two inputs.
It's useful when operations need two parameters. The example processes key-value
pairs.
package com.zetcode;
import java.util.function.BiConsumer;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> ages = new HashMap<>();
ages.put("John", 25);
ages.put("Jane", 30);
ages.put("Bob", 35);
// BiConsumer to print key-value pairs
BiConsumer<String, Integer> agePrinter =
(name, age) -> System.out.println(name + " is " + age + " years old");
// Process each map entry
ages.forEach(agePrinter);
// Inline BiConsumer
ages.forEach((k, v) -> System.out.println(k + ": " + v));
}
}
This example shows BiConsumer usage. The agePrinter takes both
map key and value. Map's forEach expects a BiConsumer. We demonstrate both
named and inline BiConsumer variants.
Specialized Consumers
Java provides specialized Consumer interfaces for primitive types to avoid
boxing overhead. These include IntConsumer, DoubleConsumer,
and LongConsumer. The example uses primitive consumers.
package com.zetcode;
import java.util.function.IntConsumer;
import java.util.function.DoubleConsumer;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] integers = {1, 2, 3, 4, 5};
double[] doubles = {1.1, 2.2, 3.3, 4.4, 5.5};
// IntConsumer example
IntConsumer squarePrinter = i ->
System.out.println(i + " squared is " + (i * i));
Arrays.stream(integers).forEach(squarePrinter);
// DoubleConsumer example
DoubleConsumer rounder = d ->
System.out.println(d + " rounded is " + Math.round(d));
Arrays.stream(doubles).forEach(rounder);
}
}
This example demonstrates primitive-specialized consumers. IntConsumer processes int values without boxing. DoubleConsumer handles double values. These specializations improve performance for primitive-heavy operations.
Source
Java Consumer Interface Documentation
In this article, we've covered the essential methods and features of the Java Consumer interface. Understanding these concepts is crucial for functional programming and collection processing in modern Java applications.
Author
List all Java tutorials.