Monday, 1 June 2015

Overview of lambda expression in Java 8

Lambda expressions (also known as closures) are one of the most important feature added in Java 8. Lambda expressions provide a clear and elegant way to represent a single abstract method interface (Functional interface) using an expression.

Though the true power of lambda expression comes into fore when used with stream API where lambda expressions can be used to provide an easy yet efficient way to perform data manipulation operations like sort, filter, map, reduce etc. But that is a topic for another post! Here I'll just stick to fundamentals of Lambda expressions, functional interface and how lambda expressions can provide implementation of the abstract method defined by the functional interface.

It is very important to have a good understanding of two terms to know lambda expression - lambda expression itself and functional interface.

What is a lambda expression?

Lambda expression in itself is an anonymous method i.e. a method with no name which is used to provide implementation of the method defined by a functional interface.
A new syntax and operator has been introduced in Java for Lambda expressions.

General form of lambda expression

(optional) (Arguments) -> body

Here you can see a new arrow operator or lambda operator which divides the lambda expression into two parts. Left side specifies parameters required by the lambda expression. Parameters are optional, if no parameters are needed an empty parenthesis can be given. Right side known as lambda body specifies the logic of the lambda expression.

Let's see some examples of the lambda expressions -

() -> 7;

This lambda expression takes no parameter, that's why empty parenthesis and returns the constant value 7.

If we have to write the equivalent Java method then it will be something like this -

int getValue(){
    return 7;
}

Some other examples -

// concatenating two strings, it has 2 string parameters and they are concatenated in lambda body
(String s1, String s2) -> s1+s2;
// Lambda expression to test if the given number is odd or not
n -> n % 2 != 0;
// Prints the passed string s to the console and returns void
(String s) -> { System.out.println(s); };

What is a functional interface?

A functional interface is an interface with only one abstract method. A functional interface is also known as SAM type where SAM stands for (Single Abstract Method). An example of functional interface would be Runnable interface which has only one method run().

Please note that from Java 8 it is possible for an interface to have default method and static method thus the stress on "only one abstract method".

Also a new annotation @FunctionalInterface has been added in Java 8, all the functional interfaces can be annotated with this annotation to ensure that they have only single abstract method.

Example of functional interface

interface IMyInterface {
    int getValue();
}

In interface IMyInterface there is only single abstract method getValue() (note that in an interface methods are implicitly abstract).

Type of Lambda expression

Lambda expression is an instance of the functional interface and provides implementation of the abstract method defined by the functional interface. Thus the functional interface specifies its target type.

Lambda supports "target typing" which infers the object type from the context in which it is used. To infer that object type from the context -

  • The parameter type of the abstract method and the parameter type of the lambda expression must be compatible. For Example, if the abstract method in the functional interface specifies one int parameter, then the lambda should also have one int parameter explicitly defined or implicitly inferred as int by the context.
  • Its return type must be compatible with the method's type.
  • Lambda expression can throw only those exceptions which are acceptable to the method.

Target typing is used in a number of contexts including the following -

  • Variable declarations
  • Assignments
  • Return statements
  • Array initializers
  • Method or constructor arguments
  • Lambda expression bodies

Example of lambda expression

interface IMyInterface {
    int getValue();
}

public class LambdaExpressionDemo {
    public static void main(String[] args) {
        // reference of the interface
        IMyInterface objRef;
       // Lambda expression
       objRef = () -> 7;
       System.out.println("Value is " + objRef.getValue());
       // Another lambda expression using the same interface reference 
       objRef = () -> 7 * 5;
       System.out.println("Value is " + objRef.getValue());
       // This line will give compiler error as target type 
       // can't be inferred 
       objref = () -> "11";
    }
}

Output

Value is 7
Value is 35

Here we have an interface IMyInterface with one method getValue() whose return type is int. Since there is only one abstract method in the interface so IMyInterface is a functional interface.

In this program it can be seen how lambda expression () -> 7 is compatible with the abstract method. Return type is int and there is no method parameter.
If you uncomment the line () -> "11"; it will give compiler error The target type of this expression must be a functional interface as in this case return type is string which is not compatible with the return type of the abstract method, lambda expression is implementing.

Also notice that the same functional interface reference is used to execute 2 lambda expressions
objRef = () -> 7; and objRef = () -> 7 * 5;
Since both of the lambda expressions are compatible with the target type so both of them can be assigned to the same reference (Does it remind you of run time polymorphism?). That's why Lambda Expression is a Poly Expression.

Example of lambda expression with a parameter

interface IMyInterface {
    boolean test(int n);
}

public class LambdaExpressionDemo {
    public static void main(String[] args) {
        IMyInterface objRef;
        // Lambda expression
        objRef = n -> (n % 2) == 0;
        System.out.println("4 is even " + objRef.test(4)); 
        System.out.println("3 is even " + objRef.test(3)); 
    }
}

Output

4 is even true
3 is even false

Here we need to note certain points -

n -> (n % 2) == 0;
In this lambda expression type is not specified explicitly as int, type is inferred from the context in which the lambda expression is executed, though type can be given explicitly too.
int n -> (n % 2) == 0; this lambda expression is also valid.

Also parenthesis around the parameter is omitted, in case of single parameter it is not necessary to enclose the parameter with parenthesis. Again it won't be invalid to do that, so (n) -> (n % 2) == 0; or (int n) -> (n % 2) == 0; are also valid.

In case of more than one parameter also type can be inferred so
(int x, int y) -> x+y; or (x, y) -> x + y;
both of these may be valid if used in a correct context.

One important point here is we can't have lambda expression where type for only one of the parameter is explicitly declared.

// Invalid lambda expression
(int x, y) -> x + y; 

Block lambda expression

Till now all the examples we have seen are single expression lambda, we also have a second type of lambda expressions known as "block lambda" where the right side of the lambda expression is a block of code.

Let's see an example, here with in the lambda expression we have a block of code for counting the words in a string without using any String function.

@FunctionalInterface
interface IMyInterface {
  int doCount(String str);
}


public class LambdaExpressionDemo {
  public static void main(String[] args) {
    // Lambda expression
    IMyInterface objRef = (str) -> {
      int c = 0;
      char ch[]= new char[str.length()];
      for(int i = 0; i < str.length(); i++){
          ch[i] = str.charAt(i);
          if(((i > 0) && (ch[i] != ' ') && (ch[i-1] == ' ')) || 
          ((ch[0] != ' ') && (i == 0)))
              c++;
      }
      return c;
    };
    
    System.out.println("Words in the string " + objRef.doCount("Lambda Expression in Java"));
    
  }
}

Output

Words in the string 4

Note here that functional interface is annotated with a @FunctionalInterface annotation, this is a new annotation added in Java 8 to be used with functional interface.

Points to note -

  • Lambda expression in itself is an anonymous method which implements the method of the functional interface.
  • The lambda expression signature must be the same as the functional interface method's signature, as the target type of the lambda expression is inferred from that context.
  • A lambda expression can throw only those exceptions or the subtype of the exceptions for which an exception type is declared in the functional interface method's throws clause.
  • The parameter list of the lambda expression can declare a vararg parameter: (int ... i) -> {};
  • A Lambda Expression Is a Poly Expression - The type of a lambda expression is inferred from the target type thus the same lambda expression could have different types in different contexts. Such an expression is called a poly expression.

That's all for this topic Overview of lambda expression in Java 8. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Lambda expression examples in Java 8
  2. Functional interfaces & lambda expression
  3. Lambda expression as method parameter
  4. Lambda expression and variable scope
  5. Lambda expression and exception handling
  6. Functional interface annotation in Java 8
  7. Method reference in Java 8

You may also like -

1 comment:

  1. That is a very good introduction of Lambda expressions in Java 8. Can you write about streams in Java 8 too.

    ReplyDelete