Java Lambda表达式中的变量范围

时间:2020-01-09 10:35:13  来源:igfitidea点击:

在本文中,我们将看到Java的lambda表达式中变量的范围是什么。这个主题很重要,因为它涉及到Java 8中增加的一个概念。在Java中最终有效。

Java Lambda表达式变量范围

Java中的Lambda表达式没有自己的范围,它的范围与其封闭的范围相同。如果在lambda表达式中定义的变量与其封闭范围中的变量具有相同的名称,则会导致错误。

例如

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

该lambda表达式实现了Comparator功能接口的compare()方法,但是由于lambda表达式中使用的参数与已定义的局部变量s1具有相同的名称,因此将导致错误。因此,该语句导致编译时错误" Lambda表达式的参数s1无法重新声明在封闭范围内定义的另一个局部变量"。

访问Lambda表达式中的封闭范围变量

由于lambda表达式不会带来新的作用域范围,因此我们可以直接访问封闭范围的字段,方法和局部变量。但是,如何使用来自封闭范围的变量存在限制。像本地和匿名类一样,lambda表达式只能访问最终或者有效最终的局部变量和封闭范围的参数。

Java中有效的最终变量是一个变量,一旦分配其值便无法对其进行修改。因此,任何包含范围的变量都可以声明为final,或者默认情况下实际上是final,并且不能在lambda表达式或者内部类中进行修改。实际上,在Java 8之前,必须将这样的字段(在内部类中使用)定义为final,而从Java 8开始,它需要放宽一点,并且不需要将此类字段显式声明为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;
    };
  }
}

在上面的示例中,在lambda表达式块中,尝试从封闭范围中修改变量的值,这会导致错误"在封闭范围中定义的局部变量num必须是最终的或者有效地是最终的"。

为什么有效地最终

现在出现了一个问题,为什么对包围范围变量进行这样的限制。使用来自封闭范围的变量的lambda表达式捕获此类变量的值。由于Java中的lambda表达式是功能接口的实例,因此在该实例中使用的包围范围的字段会捕获并使用这些字段的值。保持这些字段的状态很重要,这样就不会对其进行修改,这就是为什么要限制"有效最终"的原因。

在lambda表达式中使用this和super关键字

由于lambda表达式不会引入新的作用域,因此在lambda中将此关键字和super关键字一起使用时,将引用调用了lambda表达式所驻留的方法的同一对象。

@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));
  }
}

输出:

ToString- Hyman@theitroad
Value is- Calling from Lambda
Result is- 14

在示例中,LambdaExample类扩展了Test类,并具有方法getResult(),其中我们具有lambda表达式。如我们所见,lambda表达式可以访问this和super。从lambda调用this.toString()会打印LambdaExample的实例,而不是TestInterface的实例。