为什么不考虑Java中的局部变量"有效的最终"即使事后没有改变它?

时间:2016-02-23 08:47:42

标签: java lambda java-8 final

在方法中我有:

int x = 0
if (isA()) {
    x = 1;
} else if (isB()) {
    x = 2;
}

if (x != 0) {
    doLater(() -> showErrorMessage(x)); // compile error here
}

// no more reference to 'x' here

我不明白为什么会产生编译错误。该错误表明x不是最终的或有效的最终版,因此无法从lambda主体访问它。在x调用后,doLater没有对x进行任何修改,因此doLater的值实际上已经在调用x时确定。

我猜这个问题的答案是因为if (x != 0) { final int final_x = x; doLater(() -> showErrorMessage(final_x)); } 没有资格被称为有效最终变量。但是,我想知道原因是什么。

编译器只能创建一个临时的最终变量,有效地使代码如下:

std::string get_name() const //note the const on member function
{
    return _name; //this will work even if _name is char*, btw
}

并且一切仍然有效吗?

3 个答案:

答案 0 :(得分:14)

有效的最终意味着它可能已成为final,即它永远不会改变。这意味着有效的变量可以是final

问题在于它没有跟踪您上次更改它的时间,而是您是否更改过它。将您的if声明更改为

int x;
if (isA()) {
    x = 1;
} else if (isB()) {
    x = 2;
}  else {
    x = 0;
}

int x = isA() ? 1 : 
        isB() ? 2 : 0;

答案 1 :(得分:3)

您的x变量已经有效地最终被初始化一次,并且在任何情况下都不会再次更改。如果你只有:

int x = 0;
doLater(() -> showErrorMessage(x));

然后它会编译。

但是,添加可能更改变量值

的条件
int x = 0;
if (isA()) {
    x = 1;
} else if (isB()) {
    x = 2;
}

使变量不是最终的,因此编译错误会增加。

此外,由于这个指针方法已经实现了,因此您可以将代码重构为一个简单的if-else语句:

if (isA()) {
    doLater(() -> showErrorMessage(1));
} else if (isB()) {
    doLater(() -> showErrorMessage(2));
}

并完全摆脱x

答案 2 :(得分:1)

简短版本,如果 完全>,则无论执行哪个代码路径,

长版,引用Java语言规范4.12.4. final Variables(强调我的):

  

某些未声明为final的变量被视为有效最终

     
      
  • 如果满足以下所有条件,则声明符具有初始值设定项§14.4.2)的局部变量有效最终:      
        
    • 未声明final
    •   
    • 它永远不会出现在赋值表达式中的左侧§15.26)。 (请注意,包含初始值设定项的局部变量声明符是赋值表达式。)
    •   
    • 它永远不会作为前缀或后缀的操作数增加或减少运算符(§15.14§15.15)。
    •   
  •   

现在,您可以通过删除初始化程序使其有效最终,因为它会继续:

  
      
  • 如果满足以下所有条件,则声明符缺少初始值设定项的局部变量有效最终:      
        
    • 未声明final
    •   
    • 每当它在赋值表达式中作为左侧出现时,它肯定是未赋值的,并且在赋值之前未明确赋值;也就是说,它绝对是未分配的,并且在赋值表达式的右侧(§16 (Definite Assignment))之后没有明确赋值。
    •   
    • 它永远不会作为前缀或后缀的操作数增加或减少运算符。
    •   
  •