Master Java Supplier and Consumer Interfaces with Practical Examples
This article explains Java 8's Supplier and Consumer functional interfaces, illustrates their definitions, shows real‑world use cases such as random number generation, object creation, stream data sources, printing and validation, compares them with anonymous classes and lambdas, and connects them to design patterns and parameterized method calls.
Background
Java 8 introduced functional interfaces, among which Supplier and Consumer are fundamental building blocks for providing data and processing data.
Supplier Interface
Supplier<T>has a single abstract method T get() that takes no arguments and returns a result of type T.
package java.util.function;
/**
* Represents a supplier of results.
* @param <T> the type of results supplied by this supplier
*/
@FunctionalInterface
public interface Supplier<T> {
/** Gets a result. */
T get();
}Typical scenarios:
Random number generation:
Supplier<Integer> randomNumberSupplier = () -> new Random().nextInt(100);
System.out.println(randomNumberSupplier.get());Object creation:
Supplier<User> userSupplier = () -> new User("张三", 25);
User user = userSupplier.get();
System.out.println(user);Stream data source:
Supplier<Integer> numberSupplier = () -> (int) (Math.random() * 100);
Stream<Integer> numberStream = Stream.generate(numberSupplier);
numberStream.limit(5).forEach(System.out::println);Consumer Interface
Consumer<T>defines a single abstract method void accept(T t) that consumes a value without returning a result.
/**
* Represents an operation that accepts a single input argument and returns no result.
*/
@FunctionalInterface
public interface Consumer<T> {
/** Performs this operation on the given argument. */
void accept(T t);
}Typical scenarios:
Printing data:
Consumer<String> printConsumer = System.out::println;
printConsumer.accept("Hello, World!");Data validation:
Consumer<String> validationConsumer = s -> {
if (s.length() < 5) {
throw new IllegalArgumentException("数据长度不足");
}
// other validation logic
};
validationConsumer.accept("12345");Modifying collection elements:
Consumer<Integer> multiplyByTwo = n -> n *= 2;
numbers.forEach(multiplyByTwo);Anonymous Classes vs. Lambdas
Using an anonymous inner class to implement Supplier and Consumer requires boilerplate code, while a lambda expression provides a concise alternative:
// Anonymous class
Supplier<Integer> numberSupplier = new Supplier<Integer>() {
@Override public Integer get() { return 42; }
};
Consumer<Integer> numberConsumer = new Consumer<Integer>() {
@Override public void accept(Integer v) { System.out.println("The number is: " + v); }
};
// Lambda version
Supplier<Integer> numberSupplier = () -> 42;
Consumer<Integer> numberConsumer = v -> System.out.println("The number is: " + v);Parameterized Passing
Methods can accept Supplier or Consumer as parameters, enabling flexible data flow:
public static void methodOne() {
Supplier<String> dataSupplier = () -> "Data provided by Supplier";
methodTwo(dataSupplier);
}
public static void methodTwo(Supplier<String> supplier) {
System.out.println("Data obtained from Supplier: " + supplier.get());
} public static void methodOne() {
Consumer<String> operationConsumer = msg -> System.out.println("Executing operation on message: " + msg);
methodTwo("Hello, World!", operationConsumer);
}
public static void methodTwo(String msg, Consumer<String> consumer) {
System.out.println("Processing message: " + msg);
consumer.accept(msg);
}Design‑Pattern Connections
Strategy pattern can use Supplier to supply a strategy implementation:
public static void executeStrategy(Supplier<String> strategy) {
System.out.println("Strategy result: " + strategy.get());
}
Supplier<String> s1 = () -> "Strategy One Executed";
Supplier<String> s2 = () -> "Strategy Two Executed";
executeStrategy(s1);
executeStrategy(s2);Consumer pattern processes each element of a collection:
public static void processData(List<String> data, Consumer<String> consumer) {
data.forEach(consumer::accept);
}
Consumer<String> printer = System.out::println;
processData(Arrays.asList("A", "B", "C"), printer);Adapter pattern can wrap a legacy API with a Supplier:
class Adapter implements Supplier<String> {
private final OldApi oldApi;
Adapter(OldApi oldApi) { this.oldApi = oldApi; }
@Override public String get() { return oldApi.getData(); }
}
Supplier<String> adapter = new Adapter(new OldApi());
System.out.println(adapter.get());Comparison
Supplierprovides a value (no input, returns a result); Consumer consumes a value (takes one input, returns void). Use Supplier for lazy or deferred computation, and Consumer for side‑effect operations such as printing or modifying collections.
Learning Tips
Understand the core concepts, write small examples, apply them in real projects, create personal scenarios, study open‑source code, and regularly review.
Real‑World Usage
Many libraries already use these interfaces. For example, Spring’s RequestFactoryCustomizer implements Consumer<ClientHttpRequestFactory> to customize request factories.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Ubiquitous Tech
A ubiquitous public account for pirate enthusiasts, regularly sharing curated experiences, tech learning, and growth insights. Currently publishing articles on AI RAG customer service, AI MCP technology, and open-source design. Personal free Knowledge Planet: Awakening New World Programmer.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
