改变Lambda表达式的“自由变量”

时间:2017-05-08 10:48:03

标签: java java-8 final

我正在阅读有关Lambda表达式的this精彩文章,以下内容对我来说未清除:

  1. Lambda Expression是否将自由变量或refernse /指针的值保存到每个变量中? (我猜答案是后者,因为如果没有,变异的自由变量将是有效的。)
  2.   

    不要指望编译器捕获所有并发访问错误。该   禁止变异只适用于局部变量。

    我不确定自我试验会涵盖所有情况,因此我正在寻找有关以下内容的明确规则:

    1. Lambda Expression(静态/属性/局部变量/参数)中可以突变哪些自由变量,哪些可以在Lambda表达式中使用时突变?
    2. 在我使用它(读取或调用他的一个方法)之后,我可以在Lambda表达式块结束后突变每个自由变量吗?
    3.   

      不要指望编译器捕获所有并发访问错误。该   禁止变异只适用于局部变量。   如果   matchesis是封闭类的实例或静态变量   即使结果是未定义的,也不会报告错误。

      1. 即使我使用同步算法,突变的结果是否未定义?
      2. 更新1:

          

        自由变量 - 即不是参数且未在代码中定义的变量。

        简单来说,我可以得出结论,自由变量是不是Lambda表达式参数的所有变量,并且没有在同一个Lambda表达式中定义?

1 个答案:

答案 0 :(得分:6)

你的术语“自由变量”充其量只是误导。如果你不是在讨论局部变量(必须有效地最终被捕获),那你就是在讨论堆变量

堆变量可能是实例字段,static字段或数组元素。对于从周围上下文中对实例变量的非限定访问,lambda表达式可以(并将)通过捕获的this引用访问它们。对于其他实例字段以及数组元素,无论如何都需要通过变量进行显式访问,因此很清楚,如何访问堆变量。只能直接访问static个字段。

规则很简单,除非声明为final,否则可以在lambda表达式内部或外部修改所有规则。请记住,lambda表达式可以调用任意方法,无论如何都包含任意代码。这是否会导致问题,取决于您如何使用lambda表达式。您甚至可以创建不直接修改变量的函数的问题,而不需要任何并发性,例如

ArrayList<String> list=new ArrayList<>(Arrays.asList("foo", "bar"));
list.removeIf(s -> list.remove("bar"));
由于正在进行的迭代中的列表修改,

可能会抛出java.util.ConcurrentModificationException

同样,修改并发上下文中的变量或资源可能会破坏它,即使您确保变量本身的修改是以线程安全的方式完成的。这都是关于您正在使用的API的合同。

最值得注意的是,在使用并行Streams时,您必须意识到函数不仅由不同的线程进行评估,它们还在评估Stream的任意元素,而不管它们的遭遇顺序如何。对于Stream处理的最终结果,实现将以重新建立遭遇顺序的方式组合部分结果,如果需要,但是中间操作以任意顺序评估元素,因此您的函数不仅必须是线程安全的,而且也不依赖于特定的处理订单。在某些情况下,他们甚至可能处理对最终结果没有贡献的元素。

由于你的子弹3指的是“在一个块结束之后”,我想强调一下,你的lambda表达式中的哪个位置修改(或可感知的副作用)发生是无关紧要的。

一般来说,你最好使用没有这种副作用的功能。但这并不意味着他们一般都被禁止。