【问题标题】:Local variable in enclosing scope with different results封闭范围内的局部变量具有不同的结果
【发布时间】:2019-09-15 00:04:52
【问题描述】:

比较用于将双精度数添加到双精度数的两种运算符类型: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 不是最终的)? 如果变量声明从不改变,为什么会有不同的结果?

【问题讨论】:

  • 无法重现。第一个在所有 Java 版本中都无法编译,因为分配给已经初始化的 doub 变量使其不是有效的最终版本。你用的是哪个编译器?
  • 问题正是编译错误,请注意问题。问题在于 Java 8(在 Java 7 中你没有 UnaryOperator 不要尝试在所有以前的 Java 版本中编译,因为不存在 java.util.function 包)
  • 当我说 all Java 版本时,我指的是版本 8、9、10、11、12,即所有版本都支持 lambda,所以我当然 不是 指的是版本,7、6、5,... ---我确实注意到了这个问题,问题说DoubleUnaryOperator lambda "is OK",这是 not true,因为它无法编译。更改语句的顺序不会改变这一点。
  • 我已经删除了函数式编程标签,因为这个问题与函数式编程无关。将来,请在添加标签之前阅读标签说明。许多人订阅了特定的标签,当他们的队列充满与他们的兴趣无关的问题时,他们并不高兴。

标签: java lambda double unary-operator


【解决方案1】:

在我的情况下(Java12),DoubleUnaryOperatorUnaryOperator 都出现错误。

在您的情况下,您正在使用oo 函数结果重新分配doub,之后您尝试在下一个运算符中使用doub

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

通俗地说,如果一个局部变量的初始值从不改变,那么它实际上就是最终变量——换句话说,将它声明为最终变量不会导致编译失败。

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

如果变量是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)

【讨论】:

    【解决方案2】:

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

    信息:java:编译模块“tuts”时出错 资料:javac 1.8.0_192 用于编译java源 信息:2019 年 9 月 14 日晚上 8:10 - 构建完成,出现 1 个错误和 0 2 秒 849 毫秒内的警告 C:\Users\Philip\Code\tuts\src\test\java\tuts\UnaryOperatorTest.java 错误:(13, 60) java: 从 lambda 引用的局部变量 表达式必须是最终的或有效的最终

    问题在于,在使用 doub 的 lambda 范围内创建新范围后,您试图在外部范围内重新分配 doub

    如果您可以在 lambda 范围之外更改 doub 的值,则 lambda 的功能将变得不确定。因此,来自外部范围的值必须声明为 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);
        }
    
    }
    

    (编辑)我已经逐字复制并粘贴了您的代码。从有问题的行号(31 和 36)中可以看出,FunctionalInterface 示例都不能在 Java 8 中编译:

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

    【讨论】:

    • 问题很清楚:在一种情况下它失败了,但在另一种情况下却没有,我质疑如果变量 doub 从未改变,为什么会有不同的结果。
    • @fidudidu 查看带有屏幕截图的编辑,显示两个版本都无法编译。
    猜你喜欢
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 2013-11-01
    • 2011-08-23
    • 1970-01-01
    • 2017-03-05
    • 2015-10-08
    相关资源
    最近更新 更多