具有不同结果的封闭范围内的局部变量

Local variable in enclosing scope with different results

比较用于将双精度数添加到双精度数的两种运算符类型:DoubleUnaryOperatorUnaryOperator:

    public void test() {
        double doub = 10.0;

        // This is OK
        DoubleUnaryOperator oo = d -> {
            return doub + d;
        };          
        // Compilation error: Local variable doub defined in an enclosing scope must be final or effectively final
        UnaryOperator<Double> o = d -> {
            return doub + d; 
        };

        doub = oo.applyAsDouble(.3);
        doub = o.apply(.3);
    }  

为什么只有 UnaryOperator 会出现编译错误(doub 不是最终的)? 如果变量声明永远不变,为什么会有不同的结果?

在我的例子中 (Java12) 我在 DoubleUnaryOperatorUnaryOperator 中都出错了。

你的情况是你正在用 oo 函数结果重新分配 doub,然后你试图在下一个运算符中使用 doub

我引用的是:Difference between final and effectively final

Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.

所以,我检查后的理解是:

如果变量是 final 那么它总是可以在 lambda 表达式中使用,因为不可能重新分配它(有 - 反思,但那是另一个话题)。

声明的变量不变时出现effectively final变量

如果您将 oo 的结果分配给新声明的变量,那么您应该能够在 o.

中使用该变量

我相信您已尝试达到 10.6 值,示例如下:

double doub = 10.0;
DoubleUnaryOperator oo = d -> {
    return doub + d;
};
double ahh = oo.applyAsDouble(.3);
UnaryOperator<Double> o = d -> {
    return ahh + d;
};
System.out.println(o.apply(.3)); //The o.apply(.3) can be assigned to new variable 

任何对 doubahh 的重新分配都会导致编译错误 (Java11)

我无法重现您所说的一个编译而另一个不编译的说法。对我来说,都不编译

Information:java: Errors occurred while compiling module 'tuts' Information:javac 1.8.0_192 was used to compile java sources Information:9/14/2019 8:10 PM - Build completed with 1 error and 0 warnings in 2 s 849 ms C:\Users\Philip\Code\tuts\src\test\java\tuts\UnaryOperatorTest.java Error:(13, 60) java: local variables referenced from a lambda expression must be final or effectively final

问题是,在使用 doub. 的 lambda 中创建新作用域后,您正试图在外部作用域中重新分配给 doub

如果您可以在 lambda 范围之外更改 doub 的值,则 lambda 的功能将变得不确定。因此,来自外部范围的值必须声明为 final 或 'effectively' final(这意味着您遵守规则并且不要尝试在外部范围内重新分配给它们)。

如果您只是将结果分配给不同的变量(result,在以下示例中),您可以使这两种选择都起作用:

import org.junit.Assert;
import org.junit.Test;
import java.util.function.DoubleUnaryOperator;
import java.util.function.UnaryOperator;

public class UnaryOperatorTest {

    @Test
    public void testDoubleUnaryOperator() {
        double doub = 10.0;
        DoubleUnaryOperator doubleUnaryOperator = d -> d + doub;
        double result = doubleUnaryOperator.applyAsDouble(0.3);
        Assert.assertEquals(10.3, result, Double.MIN_VALUE);
    }

    @Test
    public void testUnaryOperator() {
        double doub = 10.0;
        UnaryOperator<Double> unaryOperator = d -> d + doub;
        double result = unaryOperator.apply(0.3);
        Assert.assertEquals(10.3, result, Double.MIN_VALUE);
    }

}

(编辑)我已经逐字复制并粘贴了您的代码。 FunctionalInterface 示例都无法在 Java 8 中编译,正如您从有问题的行号(31 和 36)中看到的那样:

然而,通过分配给 result 使 doub 有效地最终化,而不是允许代码编译: