October 22, 2022

Variable Scope in Java Lambda Expression

In this post we’ll see what is the scope of a variable with in a lambda expression in Java. This topic is important as it touches one of the concept that’s added in Java 8; Effectively final in Java.

Java lambda expression variable scope

Lambda expression in Java doesn’t have a scope of its own, it has the same scope as its enclosing scope. It results in an error if a variable is defined with in a lambda expression that has the same name as a variable in its enclosing scope.

For example-

String s1 = "Lambda";
// Error
Comparator<String> comp = (s1, s2) -> s1.length() - s2.length();

This lambda expression implements the compare() method of the Comparator functional interface but that will result in an error as the parameter used with in the lambda expression has the same name as the local variable s1 already defined. Thus this statement results in compile time error “Lambda expression's parameter s1 cannot redeclare another local variable defined in an enclosing scope”.

Accessing enclosing scope variable in Lambda expression

Since lambda expression does not introduce a new level of scoping you can directly access fields, methods, and local variables of the enclosing scope. However there is a restriction how variables from enclosing scope can be used. Like local and anonymous classes, a lambda expression can only access local variables and parameters of the enclosing scope that are final or effectively final.

An effectively final variable in Java is a variable whose value can't be modified once it is assigned. So any enclosing scope variable can either be declared as final or it is by default effectively final and it can’t be modified in lambda expression or in an inner class. Actually before Java 8 it was mandatory to define such a field (which is used in inner class) as final, Java 8 onward it is relaxed a bit and there is no need to explicitly declare such fields as final.

@FunctionalInterface
interface TestInterface{
  int calculate(int i, int j);
}
public class LambdaExample {
  public static void main(String[] args) { 
    int num = 7;
    TestInterface ref = (x, y) -> {
      // Modifying value of enclosing scope field
      num = x + y;
      return num;
    };
  }
}

In the above example with in the lambda expression block there is an attempt to modify the value of a variable from an enclosing scope that results in an error “Local variable num defined in an enclosing scope must be final or effectively final”.

Why effectively final

Now the question arises why such restriction for an enclosing scope variable. A lambda expression that uses the variables from the enclosing scope captures the values of such variables. Since lambda expression in Java is an instance of a functional interface so the fields of the enclosing scope used in such an instance captures the values of those fields and use it. It is important to maintain the state of such fields so that those are not modified that’s why this restriction of "effectively final".

Using this and super keyword in lambda expression

Since lambda expressions don’t introduce a new scope so using this keyword and super keyword with in a lambda refers to the same object that has invoked the method where the lambda expression resides.

@FunctionalInterface
interface TestInterface{
  int calculate(int i, int j);
}

class Test {
  public void showValue(String str) {
    System.out.println("Value is- " + str);
  }
}
public class LambdaExample extends Test{
  public static void main(String[] args) { 
    LambdaExample obj = new LambdaExample();
    obj.getResult();
  }
	
  public void getResult(){
    TestInterface ref = (x, y) -> {
      // Modifying value of enclosing scope field
      System.out.println("ToString- " + this.toString());
      super.showValue("Calling from Lambda");
      return x+y;
    };
    System.out.println("Result is- " + ref.calculate(8, 6));
  }
}
Output
ToString- com.knpcode.LambdaExample@66133adc
Value is- Calling from Lambda
Result is- 14

In the example, LambdaExample class extends class Test and has a method getResult() where we have a lambda expression. As you can see that lambda expression has access to the this and super. Calling this.toString() from the lambda prints an instance of LambdaExample not of TestInterface.

That's all for the topic Variable Scope in Java Lambda Expression. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment