Search This Blog

Friday 21 August 2015

Consumers in Java Lambda World

A I mentioned in the previous post, Lambda expressions have helped simplify Collection use cases. We saw that with sorting a Collection. Let me explore some more simplifications in this post.
We often need to iterate over the code in collections. General approach is to use a for loop or an enhanced for each loop. Consider the below code to display all user names
for (User user : users){
         System.out.println(user.getName() + " of age " 
            + user.getAge() + " has a salary of " + user.getSalary());
}
Java 8 has a added a new method called forEach() to the Iterable interface. All objects (such as Collection, some NIO classes etc.) that implement this interface can work with both the for each enhanced loop and the new for each method.

The new code is:
users.forEach(user -> System.out
            .println(user.getName() + " of age " + user.getAge() 
                        + " has a salary of " + user.getSalary()));
As seen from the implementation of the interface, the method takes not a Function instance but a Consumer instance. Consumer class is also a type of FunctionInterface - From the java docs:
 Represents an operation that accepts a single input argument and returns no
 result. Unlike most other functional interfaces, Consumer is expected
 to operate via side-effects.
As can be seen here we are not really doing any transformation, or returning some output as we did with the Function instance for comparable, instead here we simply consume the string and return nothing.
The code need not be a one liner, could also be a block code.
     
      Set<User> highIncomeUsers = new HashSet<>();
      users.forEach(user -> {
         if (user.getSalary() > 3000) {
            highIncomeUsers.add(user);
         } else {
            System.out.println(user.getName() + " with salary of " 
                                   + user.getSalary() + " was skipped");
         }
      });
      System.out.println("HIGH INCOME USERS");
      highIncomeUsers.forEach(user->System.out.println(user.getName() 
                                         + " with salary of " + user.getSalary()));
As can be seen here, we are introducing a hash set that results that is used outside the scope of this call - kind of side effects that we saw in the class comments.
It is not that we are restricted to using Consumer or other functional interfaces only as parameters to other methods.
Consider the below code:
      Set<User> highIncomeUsers = new HashSet<>();
      Consumer<User> highIncomeUserCollector = (user) -> {
         if (user.getSalary() > 3000) {
            highIncomeUsers.add(user);
         } else {
            System.out.println(user.getName() + " with salary of " 
                                 + user.getSalary() + " was skipped");
         }
      };
      highIncomeUserCollector.accept(users.get(0));
Just to be clear, Consumer is one type of function object created by the compiler from Lambda expression. It is generated for expressions associated with taking input but not creating any return value.

No comments:

Post a Comment