Saturday, 16 August 2014

Lambda Expressions part2 : Throwing Exceptions And Capturing Effective Variables

Throwing Exceptions :

    First we will start by examining a simple Lambda Expression for creating a Thread.
 
Runnable r = () -> {
    System.out.println("Thread is going to sleep");
    Thread.sleep(1000);
}

Guess, what is the result of this lambda expressions. It throws a compile time error. Calling sleep() on thread throws Interrupted Exception.

We have two options to fix this issue.

1. Catching the exception by enclosing the statement "Thread.sleep(1000) " inside a try / catch block. Just like below.

try {
    Thread.sleep(1000);
}catch(InterruptedException ie) {
    ie.printStackTrace();
} 
2. Assigning this lambda expression to a functional interface, whose abstract method can throw the exception. Unfortunately the run() cannot throw any exception.
Note that, the prerequisite for a lambda expression to throw a checked exception is that it must be compatible with the exceptions provided after the throws clause of method declaration in the functional interface.

Accessing Variables And Methods From Enclosing Scope :

 Variables defined in the enclosing scope of a lambda expressions are accessible any where in the lambda expression.

Accessing Variables From Enclosing Class :

Lambda expressions can have access to both instance, static variables and methods defined by the enclosing class. It has even access to "this" variable which is an instance of enclosing class. A lambda expression can also set the value of an instance or static variables.

Here is an example.
 
package com.speakingcs.Lambda;

public interface SimpleFI {
 
    int aFunc();

}

// The enclosing class is 

package com.speakingcs.Lambda;

public class SimpleLambda {
 
    static int a = 10;
 
    public static void main(String[] args) {
  
    // accessing static variable a 
  
    SimpleFI sf = () -> a;
    System.out.println(sf.aFunc());
  
    // setting value for a 
  
    sf = () -> {
       return a = 20;
    };  
  
 System.out.println(sf.aFunc());
  }

}

Accessing Local Variables From Enclosing Method :

How ever in Java 7, while using anonymous inner classes, if you want to acess a variable from the enclosing scope of a method, you have to explicitly make the variable final. Otherwise you will get a compilation error saying " local variable is defined in enclosing scope must be final ".
 
void createThread() {
    final int a = 5; // local variable must be final
  
    Thread t = new Thread(new Runnable() {

    @Override
    public void run() {
    System.out.println(" a is " + a);
    try {
       Thread.sleep(1000);
    } catch (InterruptedException e) {      
        e.printStackTrace();
      }    
    }
  }); 
  
}
This restriction is relaxed a bit in Java 8. Now there is no need for a variable to be final, but it has to be effectively final. A variable is called effectively final if and only if, it is assigned only once. It can not be modified by lambda expression.

public void effecLambda() {
     int a = 10; // effectively final variable
     //a = 20; reassigning variable results in compilation error
     SimpleFI sf = () -> a;  
}
Here actually, we are capturing value of variable. Modifying the variable either inside or outside of lambda expression would make the variable to loose it's effective final status, thus results compile time error.

0 comments:

Post a Comment

Note: only a member of this blog may post a comment.