Consumer and Supplier in Java 8

Salitha Chathuranga
4 min readJul 11, 2022

--

Let’s learn some Java 8 things…

Hey all! Hope you are doing well…This is my next article on Java 8 series…If you still could not see my previous Java 8 articles, you can get it from here. 😎

Java 8 Features

8 stories

Let’s focus on today’s article.

Consumers

Java 8 provides us Consumer functional interface to be used in places where we pass an argument but do not need to return anything. Lets look at the interface code.

@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);

default Consumer<T> andThen(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
return (var2) -> {
this.accept(var2);
var1.accept(var2);
};
}
}

Following functional interface paradigm, it has only 1 abstract method called accept(T var). Apart from that we have a default method also following Java 8, to join consumers.

Java 8 interface changes: https://medium.com/dev-genius/interface-changes-in-java-8-feae3ab6970d

We have to pass the Type as a Generic to the interface which is the type of the objects in the list or stream. It is dependent on the usage. It can be String / Integer / Custom Object, etc…Let’s see how to use it.

  • Imagine we need to print a list of numbers..Here we need each number as a parameter/argument but we do not return anything which means we can use a consumer. Since list has integers, we must use Integer as Type.
List<Integer> list = Arrays.asList(2, 4, 1, 6, 9, 3, 8);
Consumer<Integer> c1 = x -> System.out.println(x);
list.forEach(c1);

Since we have Lambda expressions ❤️, we have the ability to provide the implementation runtime for any functional interface. Further more we can use method referencing also in implementation.

Consumer<Integer> c1 = System.out::println;

You will see the list items are printed when this code is executed. This can be considered as the enhancement done for FOR LOOPS, in Java 8.

  • Imagine we need to print the numbers multiplied by 5. Its easy…
list.forEach(x -> System.out.println(x * 5));

Bi Consumers

The purpose of the BiConsumer is also same as Consumer. But the implementation is different. As its name depicts, we can accept 2 parameters using this class. They can have different Types OR same Type.

@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T var1, U var2);
}
  • Imagine we need to process the items in a Map with some formatting. This is the place we can use Bi consumers. Let’s see…
BiConsumer<String, Integer> biConsumer = (x, y) -> System.out.println(x + ":" + (y * 3));
Map<String, Integer> map = new HashMap<>();
map.put("k1", 100);
map.put("k2", 200);
map.put("k3", 300);
map.forEach(biConsumer);

Result:

k1:300
k2:600
k3:900

andThen() Method 💥

This method can be used to combine multiple consumers and apply the logic. We can combine any number of consumers. See the below example. Nothing hard is there. This method is available in both Consumer and BiConsumer interfaces.

List<Integer> list = Arrays.asList(2, 4, 1, 6, 9, 3, 8);
Consumer<Integer> c1 = x -> System.out.print(x + ":");
Consumer<Integer> c2 = x -> System.out.print(x * 5 + "-");
Consumer<Integer> c3 = x -> System.out.print("Hello ");
list.forEach(c1.andThen(c2).andThen(c3));

Result:

2:10-Hello 4:20-Hello 1:5-Hello 6:30-Hello 9:45-Hello 3:15-Hello 8:40-Hello

Suppliers

In contrast to Consumers, Supplier is NOT taking any argument, But returns something. Lets look at the interface code.

@FunctionalInterface
public interface Supplier<T> {
T get();
}

We have to pass the Type as a Generic to the interface as previous, but this time it represents the returning type. It can be String / Integer / Custom Object, etc…

We can wrap the things we need as a supplier. Then we can call get() method to retrieve the wrapped value. Let’s see..

Supplier<String> s1 = () -> "Hello";     // lambda expression
System.out.println(s1.get());
Supplier<LocalDate> s2 = LocalDate::now; // method referencing
System.out.println(s2.get());

But what is the actual usage???

Imagine you have parameters stored in database that you want to keep in constant for whole application. You need to wrap it beforehand and you will be accessing it later. That value needs to be initialized once and won’t change until a redeployment. Here we can use a supplier.

Saying that, this will never improve performance! This is just to make concise code. The main aim of Java 8 functional programming paradigm is to reduce lines of codes as I think.

We don’t have any default methods in Supplier interface. And we don’t have Bi suppliers in Java…!!! 😅

So this is all about Consumers and Suppliers. You can organize the code in an advanced and much prettier way by mixing it little bit functional programming concepts like these. That’s a really cool thing!

Try this out and let me know your experience.

Bye bye!!! 😍

--

--

Salitha Chathuranga

Associate Technical Lead at Sysco LABS | Senior Java Developer | Blogger