带有 lambda 表达式的 Java 闭包

当 lambda 表达式引用封闭范围(全局或局部)的变量时,将创建 lambda 闭包。执行此操作的规则与内联方法和匿名类的规则相同。

** 来自 lambda 中使用的封闭范围的局部变量必须是 final。使用 Java 8(支持 lambdas 的最早版本),它们不需要在外部上下文中声明 final,但必须以这种方式处理。例如:

int n = 0; // With Java 8 there is no need to explicit final
Runnable r = () -> { // Using lambda
    int i = n;
    // do something
};

只要 n 变量的值没有改变,这是合法的。如果你尝试在 lambda 内部或外部更改变量,你将收到以下编译错误:

“从 lambda 表达式引用的局部变量必须是最终的有效的最终 ”。

例如:

int n = 0;
Runnable r = () -> { // Using lambda
    int i = n;
    // do something
};
n++; // Will generate an error.

如果有必要在 lambda 中使用更改变量,通常的方法是声明变量的 final 副本并使用副本。例如

int n = 0;
final int k = n; // With Java 8 there is no need to explicit final
Runnable r = () -> { // Using lambda
    int i = k;
    // do something
};
n++;      // Now will not generate an error
r.run();  // Will run with i = 0 because k was 0 when the lambda was created

当然,lambda 的主体看不到原始变量的变化。

请注意,Java 不支持真正的闭包。无法以允许它查看实例化环境中的更改的方式创建 Java lambda。如果要实现观察或更改其环境的闭包,则应使用常规类来模拟它。例如:

// Does not compile ...
public IntUnaryOperator createAccumulator() {
    int value = 0;
    IntUnaryOperator accumulate = (x) -> { value += x; return value; };
    return accumulate;
}

由于前面讨论过的原因,上面的例子不会编译。我们可以解决编译错误,如下所示:

// Compiles, but is incorrect ...
public class AccumulatorGenerator {
    private int value = 0;

    public IntUnaryOperator createAccumulator() {
        IntUnaryOperator accumulate = (x) -> { value += x; return value; };
        return accumulate;
    }
}

问题是这破坏了 IntUnaryOperator 接口的设计合同,该接口声明实例应该是功能性的和无状态的。如果将这样的闭包传递给接受功能对象的内置函数,则可能导致崩溃或错误行为。封装可变状态的闭包应该作为常规类实现。例如。

// Correct ...
public class Accumulator {
   private int value = 0;

   public int accumulate(int x) {
      value += x;
      return value;
   }
}