Thursday, May 01, 2014

What's New in Java 8 - Lambda Expressions

Introduction :

Lambda Expressions enable us to treat functionality as a method argument or code as data . Well, What is functionality as a method argument? Did we already use this before? Yes, the answer is yes, we already used this concept before with anonymous classes. In anonymous classes case, we are usually trying to pass functionality as an argument to another method. Lambda expressions are also to do this. Although  anonymous classe is often more concise than a named class, for classes with only one method, even an anonymous class seems a bit excessive and cumbersome.

Usage :

First let us  discuss the usage of  the Lambda Expressions then will go through the syntax of Lambda Expressions.Suppose that you are creating an application for car sellers. You are asked to create a feature that search cars by certain criteria.
Car class can be represented as follows.
public class Car{
    public enum Color {
        BLACK, WHITE, RED
    }
    public String modelName;
    public Color color;
    public int engineCC;
    public void printModelName() {
        // ...
    }
}
Now you need to implement a feature that search for cars that match one characteristic such as engine cc.One simplistic approach is  create a method that  searches for cars that match one characteristic, such as engine cc. The following method prints Model name that are greater than the specified engine CC.
public void searchCarsGreaterThanCC(List<Car> cars, int cc) {
       for (Car c : cars) {
           if (c.engineCC > cc) {
                   c.printModelName();
        }
    }
}
This approach can potentially make your application brittle,Suppose that you upgrade your application and change the structure of the Car class such that it contains different member variables; perhaps the class records and engineCC with a different data type. You would have to rewrite the logic  to accommodate this change. In addition, this is very restrictive, for example if you wanted to get the models with lesser that certain engineCC. What will you do?
So will go for more generic search method that get cars with in the specified range of engine CC.
public void searchCarsWithinCCRange(
          List<Car> cars, int lowCC, int highCC) {
          for (Car c : cars) {
               if (lowCC <= c.engineCC && c.engineCC < highCC) {
                        c.printModelName();
          }
     }
}

This approach also restrictive, What if you want to get a specified color of cars or combinations of color and engineCC?  You can  separate the code that specifies the criteria for which you want to search in a different class, for this create an interface SerachCar with one  filter method and implement the method for any criteria.The following method prints model names that match search criteria that you specify:
public void searchCars(
       List<Car> cars, SerachCar filter) {
             for (Car c : cars) {
                   if (filter.filter(c)) {
                     c.printModelName();
                 }
          }
}
This method checks each Car,whether it satisfies the search criteria specified in the SerachCar parameter filter by invoking the method filter.filter. If the method filter returns a true value, then the method prints the model name of the car.To specify the search criteria, you need to  implement the SerachCar interface:
interface SerachCar { 
         boolean filter(Car c);
}

The following class implements the SerachCar interface by specifying an implementation for the method filter. This method filters cars that are Red and above  500engine CC.
class SerachCarWithRedAndAbove500CC implements SerachCar {
         public boolean filter(Car c) {
              return c.color == Car.Color.RED && 
             c.engineCC >= 500 ;
      }
}
then we can use above class to search the cars as follows
             searchCars(cars, new SerachCarWithRedAndAbove500CC ());
Although this approach is less brittle—you don't have to rewrite methods if you change the structure of the Car, but you still have additional thing you need to do is  a local class for each search  is to create. Instead writing classes for each criteria  you can use an anonymous class.
The second argumnet of the method searchCars is an anonymous class that filters members that are eligible for Red and above 500CC car.
searchCars (cars,
               new SerachCar() {
                       public boolean filter(Car c) {
                                  return c.color == Car.Color.RED  
                                            && c.engineCC >= 500 ;
                       }
              }
  );
By this approach, you  no need  to create a new class for each search that you want to perform. However, If you the observe the syntax of anonymous classes is bulky as you need to implement the method with class as as argument.In this case, you can use a lambda expression instead of an anonymous class.So we came to our actual topic now.
The SerachCar interface is a functional interface. A functional interface is any interface that contains only one abstract method. (A functional interface may contain one or more default methods or static methods.) Because a functional interface contains only one abstract method, you can omit the name of that method when you implement it. To do this, instead of using an anonymous class expression, you use a lambda expression.
The following is the actual syntax of lambda expressions, method invocation is shown here with lambda expressions instead anonymous class
       serachCars(cars,
               (Car c) -> c.color == Car.Color.RED &&
                                c.engineCC >= 500
      );
If you observe the method invocation with anonymous class and lambda expressions, with  anonymous class invocation you are specifying the  interface name and also method name with implementation. But with lambda expression they are not required, then how does compiler  understand?,Well. The answer is very simple, compiler knows the interface by method signature and that interface has only one method as it is functional interface so only one method should be  implement. the code what you are passing as a argument is nothing but the implementation of the method.

Syntax:

Did you get the meaning of the lambda expressions which is used in SerachCar method invocation?
The meaning is very simple to understand, the SearchCar method's  second argument is functional interface(only one default method) and the right hand side of the arrow token in lambda expression is the implementation code of  that default method and the left hand side of the arrow token  is the argument of the default method. You can compare the same with anonymous  class
public boolean filter(Car c) {
}
is
     (Car c) -> 
and
       public boolean filter(Car c) { 
                return c.color == Car.Color.RED &&
                           c.engineCC >= 500 
     }
   is
(Car c) -> c.color == Car.Color.RED && c.engineCC >= 500

More on Lambda Expressions Syntax: 

  • If  the method as more than one argument then a comma-separated list of formal parameters enclosed in parentheses.  (a, b) -> a + b;
  • You can omit the data type of the parameters in a lambda expression.
  • (c) -> c.color == Car.Color.RED && c.engineCC >= 500
Note: How does compiler resolve the data type? Again the answer is same by the method signature of default method in functional interface
  • You can also omit the parentheses if there is only one parameter.
     c -> c.color == Car.Color.RED && c.engineCC >= 500
  • The right side of arrow token, we can call that as body,which consists of a single expression or a statement block.
  • If you specify a single expression, then the Java runtime evaluates the expression and then returns its value.Other wise, you can use a return statement:
                c -> {
                      return c.color == Car.Color.RED && c.engineCC >= 500;
    }
  • If a return statement is not an expression; in a lambda expression, you must enclose statements in braces ({}).
  • However, you do not have to enclose a void method invocation in braces. 
  • Lambda expression looks a lot like a method declaration, you can consider lambda expressions as anonymous method (methods without a name).
 An another example of lambda expressions that take more than one formal parameter:
public class Calculator {
    interface IntegerMath {
        int operation(int a, int b);   
    }
    public int operateBinary(int a, int b, IntegerMath op) {
        return op.operation(a, b);
    } 
    public static void main(String... args) { 
        Calculator myApp = new Calculator();
        IntegerMath addition = (a, b) -> a + b;
        IntegerMath subtraction = (a, b) -> a - b;
        System.out.println("40 + 2 = " +
            myApp.operateBinary(40, 2, addition));
        System.out.println("20 - 10 = " +
            myApp.operateBinary(20, 10, subtraction));    
    }
}
The method operateBinary performs a mathematical operation on two integer operands. The operation itself is specified by an instance of IntegerMath. The example defines two operations with lambda expressions, addition and subtraction. The example prints the following:
       40 + 2 = 42
       20 - 10 = 10
More examples and usages with new features of the java 8 on  Lambda expressions will be discussed in my next posts. Thank you for reading this post.

No comments:

Post a Comment

Distributed Transactions

What is a distributed transaction?  Transactions that span over multiple physical systems or computers over the network, are simply termed D...