By looking at the Consumer<T> interface definition, you can <indexentry content="standard functional interfaces:Consumer">guess already that this interface has an abstract method that accepts a parameter of type T and does not return anything. Well, when only one type is listed, it may define the type of the return value, as in the case of the Supplier<T> interface. But the interface name serves as a clue: the consumer name indicates that the method of this interface just takes the value and returns nothing, while the supplier returns the value. This clue is not precise but helps to jog the memory.
The best source of information about any functional interface is the java.util.function package API documentation (https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/function/package-summary.html). If we read it, we learn that the Consumer<T> interface has one abstract and one default method:
- void accept(T t): Applies the operation to the given argument
- default Consumer<T> andThen(Consumer<T> after): Returns a composed Consumer function that performs, in sequence, the current operation followed by the after operation
It means that, for example, we can implement and then execute it as follows:
Consumer<String> printResult = s -> System.out.println("Result: " + s);
printResult.accept("10.0"); //prints: Result: 10.0
We can also have a factory method that creates the function, for example:
Consumer<String> printWithPrefixAndPostfix(String pref, String postf){
return s -> System.out.println(pref + s + postf);
Now we can use it as follows:
printWithPrefixAndPostfix("Result: ", " Great!").accept("10.0");
//prints: Result: 10.0 Great!
To demonstrate the andThen() method, let's create the class Person:
public class Person {
private int age;
private String firstName, lastName, record;
public Person(int age, String firstName, String lastName) {
this.age = age;
this.lastName = lastName;
this.firstName = firstName;
}
public int getAge() { return age; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String getRecord() { return record; }
public void setRecord(String fullId) { this.record = record; }
}
You may have noticed that record is the only property that has a setting. We will use it to set a personal record in a consumer function:
String externalData = "external data";
Consumer<Person> setRecord =
p -> p.setFullId(p.getFirstName() + " " +
p.getLastName() + ", " + p.getAge() + ", " + externalData);
The setRecord function takes the values of the Person object properties and some data from an external source and sets the resulting value as the record property value. Obviously, it could be done in several other ways, but we do it for demo purposes. Let's also create a function that prints the record property:
Consumer<Person> printRecord = p -> System.out.println(p.getRecord());
The composition of these two functions can be created and executed as follows:
Consumer<Person> setRecordThenPrint = setRecord.andThen(printPersonId);
setRecordThenPrint.accept(new Person(42, "Nick", "Samoylov"));
//prints: Nick Samoylov, age 42, external data
This way, it is possible to create a whole processing pipe of the operations that transform the properties of an object that is passed through the pipe.