06 - The consumer Interface

“Whatever goes in, never comes out” could be the Hollywood movie line to define a consumer, which is no more than a function that receives an argument, consumes it and returns nothing.

In this article I will talk about the Consumer functional Interface. Being a functional interface means it can be used as target type for lambda expressions.

A consumer is quite easy to explain. It is used whenever we need an object to be consumed. Consumed in this context means that a function receives an object as an input argument, eventually applies some kind of operation to that object, but nothing is returned from that function. In this cases, we say that the object has been consumed.

Let’s take a quick look to the Consumer interface source code,

java.util.function.Consumer.java
1
2
3
4
5
6
7
8
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

The abstract method accept(), is responsible for receiving the object, performing whatever operation is defined in the lambda expression, and returns no value.
The andThen() method is a little more trickier. When applied, it returns a new consumer which represents the aggregation of both operations defined in both consumers.

We can write a quick example to demonstate the Consumer accept()

ConsumerExampleOne.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ConsumerExampleOne {

public static void main(String[] args) {

Consumer<Integer> consumer=x->System.out.println(x);
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

for(Integer n:integerList) {
print(n,consumer);
}
}

public static void print(Integer n,Consumer<Integer> consume) {
consume.accept(n);
}
}

in the above example, the Consumer, is passed to the print() method together with an Integer and then just print’s the value.

We can also observe the behaviour of the andThen() method from the Consumer Interface, which combines two expressions,

ConsumerExampleTwo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ConsumerExampleTwo {

public static void main(String[] args) {
List<String> wordList = Arrays.asList("Al", "Bc", "Cs", "Dr", "Ft");
Consumer<String> consumer1 = x -> System.out.println("consumer One :" + x.toLowerCase());
Consumer<String> consumer2 = x -> System.out.println("Consumer Two :" + x.toUpperCase());

for (String w : wordList) {
print(w, consumer1, consumer2);
}
}

public static void print(String s, Consumer<String> consumerOne, Consumer<String> consumerTwo) {
Consumer<String> combinedConsumer = consumerOne.andThen(consumerTwo);
combinedConsumer.accept(s);
}
}

When run the above code will print,

consumer One :al
consumer Two :AL
consumer One :bc
consumer Two :BC
consumer One :cs
consumer Two :CS
consumer One :dr
consumer Two :DR
consumer One :ft
consumer Two :FT

Two consumers are created, consumer1 and consumer2. Both are dispatched to the print() method where they are combined originating the combinedConsumer reference. When the the accept() method is called on the combinedConsumer both operations defined in the consumer1 and consumer2 are applied.

A better example than just print something

As mentioned above, a consumer, can apply any transformation we want to an object. In the next example, we simulate a bank that charges a service fee every time a customer makes a deposit with a value lower than 200. We can use a combination of a predicate and a consumer to handle the job,

the Account class

Account.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Account {

private int accountNumber;
private double AccountBalance;

public Account(int accountNumber, double InitialDeposit) {
this.accountNumber=accountNumber;
this.AccountBalance=InitialDeposit;
}
public int getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(int accountNumber) {
this.accountNumber = accountNumber;
}
public double getAccountBalance() {
return AccountBalance;
}
public void setAccountBalance(double accountBalance) {
AccountBalance = accountBalance;
}
}

The AccountProcessor Class

AccountProcessor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.function.Consumer;
import java.util.function.Predicate;

public class AccountProcessor {

public void deposit(Account account,double amount,Predicate<Double> lowValueDeposit,Consumer<Account> consumer) {
if (lowValueDeposit.test(amount)) {
consumer.accept(account);//it is a low deposit, reduce fee from balance.
System.out.println("Deposit Fee applied to account Number "+ account.getAccountNumber());
}else {
System.out.println("No deposit Fee applied");
}
account.setAccountBalance(account.getAccountBalance()+amount);
}
public void printBalance(Account account) {
System.out.println(account.getAccountBalance());
}
}

And the App that will do some work,

App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.function.Consumer;
import java.util.function.Predicate;

public class App {

public static void main(String[] args) {

Account accountOne = new Account(1, 0);
Account accountTwo = new Account(2, 0);
Account accountThree = new Account(3, 0);

Predicate<Double> isLowDeposit = x -> x < 200;
Consumer<Account> lowDepositServiceFee = acc -> acc.setAccountBalance(acc.getAccountBalance() - 2.50);

AccountProcessor accountProcessor = new AccountProcessor();
accountProcessor.deposit(accountOne, 201.0, isLowDeposit, lowDepositServiceFee);
accountProcessor.deposit(accountTwo, 180.0, isLowDeposit, lowDepositServiceFee);
accountProcessor.deposit(accountThree, 80.0, x -> x < 200, acc -> acc.setAccountBalance(acc.getAccountBalance() - 2.50));

accountProcessor.printBalance(accountOne);
accountProcessor.printBalance(accountTwo);
accountProcessor.printBalance(accountThree);
}
}

When run it prints,

No deposit Fee applied
Deposit Fee applied to account Number 2
Deposit Fee applied to account Number 3
201.0
177.5
77.5

Most of the above example is code just to make the simulation run. The relevant stuff for the example is the lowDepositServiceFee Consumer and the isLowDeposit Predicate references that are created in line 12 and 13 and later passed to the accountProcessor.deposit() method. This is where the predicate and the consumer do their magic in line 7 and line 8. Obviously this only works as an example, so if you do own a bank do not use this, as we are not even checking if the deposit is not negative !!! :)

Also note that in line 18, instead of passing the isLowDeposit reference, we passed the lambda expression directly, just to show that it also can be done in that way.

In the next post I will talk about the Supplier interface.

‘till then happy coding.

Share