有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java:为什么Java不能在lambda表达式的封闭范围内自动“完成”局部变量?

我认为这是函数式编程的标准部分。。我的问题是为什么编译器不能在lambda语句开始之前自动将变量的副本声明为final

import java.util.stream.IntStream;

public class Example
{
  public static void main( String args[] )
  {
    int i = 5;
    i = 6;
    IntStream.range(0, 10).mapToLong( j-> i * j ).sum();
  }


}

失败。。。“我在封闭范围内定义的局部变量必须是final或实际上是final”,而编译器似乎应该足够聪明,可以这样做

import java.util.stream.IntStream;

public class Example
{
  public static void main( String args[] )
  {
    int i = 5;
    i = 6;
    final int _i = i;
    IntStream.range(0, 10).mapToLong( j-> _i * j ).sum();
  }


}

编译器可以强制lambda函数永远不会修改最终确定的变量


共 (2) 个答案

  1. # 1 楼答案

    但是,如果lambda被传递到某个异步使用它的地方(即,它可以在当前函数结束后运行),并且在创建lambda之后修改函数范围中的变量i,该怎么办

    int i = 5;
    i = 6;
    useLambdaAsynchronously( j-> i * j );
    i = 7;
    

    lambda仍然会为i捕获一个值6,但是i(应该是同一个变量,因为您只声明了一个i)现在在另一个作用域中的值为7。这是不一致的,因为程序员应该期望一个变量一次只有一个值。如果稍后运行lambda,它仍将使用i的值6,即使之前已将7分配给i

    为避免此问题,编译器需要确保在创建lambda后,未在lambda中分配变量,且未在原始函数范围中分配变量。但这将导致这样一种情况,即在函数的前面允许赋值,但在同一函数的后面不允许赋值(仅仅因为它已被lambda捕获),这也可能会让程序员感到惊讶。为了简单起见,Java只是不允许在任何地方进行赋值

  2. # 2 楼答案

    好吧,如果变量i实际上是最终变量,编译器实际上会这样做

      public static void main( String args[] )
      {
        int i = 5;
        IntStream.range(0, 10).mapToLong( j-> i * j ).sum();
      }
    

    然而,对于第二个赋值i = 6;,您使它不是“有效的最终赋值”,您表明您实际上希望它是可变的

    那么,为什么在这种情况下,编译器应该创建变量的最终副本,尽管您发出了希望它是可变的信号