在try / catch语句

时间:2017-12-22 13:31:52

标签: java lambda language-lawyer final jls

以下代码无法使用javac 1.8.0_144和ecj:

进行编译
private LongSupplier foo() {
    long fileSize;
    try {
        fileSize = canThrow();
    } catch (IOException e) {
        fileSize = 42;
    }

    LongSupplier foo = () -> 1 + fileSize;
    return foo;
}

我想知道这是否是编译器中的错误。 The definition of effectively final in the JLS是:

  

某些未声明为final的变量被认为是有效的最终变量:

     
      
  • 如果满足以下所有条件,则声明符具有初始值设定项(第14.4.2节)的局部变量实际上是最终的:

         
        
    • 未宣布为最终版。

    •   
    • 它永远不会出现在赋值表达式(第15.26节)的左侧。 (注意包含
      的局部变量声明符   初始化程序不是赋值表达式。)

    •   
    • 它永远不会作为前缀或后缀增量或减量运算符的操作数出现(§15.14,§15.15)。

    •   
  •   
  • 如果满足以下所有条件,则声明者缺少初始值设定项的局部变量实际上是最终的:

         
        
    • 未宣布为最终版。

    •   
    • 每当它在赋值表达式中作为左侧出现时,它肯定是未分配的并且没有明确赋值   在转让之前;也就是说,它绝对是未分配的而不是   在作业的右侧之后明确分配   表达式(§16(定义分配))。

    •   
    • 它永远不会作为前缀或后缀的操作数增加或减少运算符。

    •   
  •   
  • 为了达到目的,方法,构造函数,lambda或异常参数(§8.4.1,§8.8.1,§9.4,§15.27.1,§14.20)被视为   确定它是否是有效的最终,作为局部变量
      其声明者有一个初始化器。

  •   

我的解释是,在第2节中,允许try / catch块中的赋值,因为fileSize在赋值之前肯定是未分配的。

我认为解释拒绝代码的原因是:

  • fileSize在try block
  • 之前肯定是未分配的
  • 在fileSize = canThrow()
  • 之后分配了文件大小(当然,似乎16.1.8并不关心作业中的异常)
  • fileSize在try块
  • 之后分配
  • fileSize在catch块之前并不是绝对未分配的,因此在catch块中的赋值之前不一定是未分配的。
  • 因此,4.12.4第2条不适用于此

这是对的吗?

2 个答案:

答案 0 :(得分:2)

“有效最终”的定义指出添加final修饰符不应改变任何内容。让我们这样做,并得到一个更明确的错误:

error: variable fileSize might already have been assigned
                    fileSize = 42;
                    ^

因此,这与Final variable assignment with try/catch完全相同(也使用第二个最终变量提供了解决方法),即变量出现在赋值的左侧,这意味着它并非绝对未分配。

答案 1 :(得分:2)

(为了良好的顺序,try-catch与问题无关:他们只是认为catch异常参数被认为是final。)

意图是"有效的最终"建立在两个线程的基础上,每个线程都具有同名变量的副本。这两个线程/变量的生命周期是不同的。他们希望防止一个线程发生变化,这需要进行一些同步和生命检查。

所以他们绝对不想要任务。作为语言设计的决定。

canThrow could中的内部线程使用fileSize仍然为 后,将另一个变量fileSize设置为42. 我认为你考虑一个引发的异常来表示另一个线程已经死亡。

在这种情况下,你想要的是 Future / FutureTask 等。