具有不同结果的封闭范围内的局部变量
Local variable in enclosing scope with different results
比较用于将双精度数添加到双精度数的两种运算符类型:DoubleUnaryOperator 和 UnaryOperator :
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) 我在 DoubleUnaryOperator
和 UnaryOperator
中都出错了。
你的情况是你正在用 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
任何对 doub
或 ahh
的重新分配都会导致编译错误 (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
有效地最终化,而不是允许代码编译:
比较用于将双精度数添加到双精度数的两种运算符类型:DoubleUnaryOperator 和 UnaryOperator
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
在我的例子中 (Java12) 我在 DoubleUnaryOperator
和 UnaryOperator
中都出错了。
你的情况是你正在用 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
任何对 doub
或 ahh
的重新分配都会导致编译错误 (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
有效地最终化,而不是允许代码编译: