难道有效的最终/最终限制无用吗?

时间:2018-05-25 12:32:15

标签: java jvm

我无法理解为什么java语言的实现者为什么在lambda中使用并从函数作用域传递的变量必须是最终的。

我反编译了这段代码:

public class Main {
    @FunctionalInterface
    interface Test {
        void method(int t);
    }

    static void test(Test t) {
        t.method(3);
    }

    public static void main(String... args) {
        int a = 3;
        test((i)-> {
            System.out.println(a + i);
        });
    }
}

以及编译器所做的是复制该变量,就好像它是通过构造函数传递一样。我得到了这3个课程:

1:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import Main.1;
import Main.Test;

public class Main {
    public Main() {
    }

    static void test(Test var0) {
        var0.method(3);
    }

    public static void main(String... var0) {
        byte var1 = 3;
        test(new 1(var1));
    }
}

2:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import Main.Test;

final class Main$1 implements Test {
    Main$1(int var1) {
        this.val$a = var1;
    }

    public void method(int var1) {
        System.out.println(this.val$a + var1);
    }
}

3:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

@FunctionalInterface
interface Main$Test {
    void method(int var1);
}

为什么实施者无法复制变量而不管它是否被修改,所以我们可以这样做:

public class Main {
    @FunctionalInterface
    interface Test {
        void method(int t);
    }

    static void test(Test t) {
        t.method(3);
    }

    public static void main(String... args) {
        int a = 3;
        test((i)-> {
            a += 1; // allow modification, a is copied anyway, why not?
            System.out.println(a + i);
        });
    }
}

1 个答案:

答案 0 :(得分:7)

这没有技术原因。只是如果你允许在lambda中使用非final字段,那么你可以编写看起来很好但实际上不起作用的代码。

例如:

void printSum(Collection<Integer> numbers) {
  int sum = 0;
  numbers.forEach(i -> sum += i);
  System.out.println(sum);
}

目前,编译器不允许您这样做,因为您无法访问lambda中的非最终sum。正如您所指出的那样,变量无论如何都会被复制到lambda中,所以它可以允许它。

如果确实如此,则此代码将编译,但始终打印0,因为只修改了lambda中的sum - 副本而不是“真实的”。

只允许“有效最终”变量被引用是一个很好的折衷方案,不需要final关键字无处不在,同时仍然避免像这样的误解。