代码不会编译因为最终变量“可能”(但不能)被分配两次?

时间:2013-06-30 23:26:25

标签: java variable-assignment

看看这个简单的Java代码:

class A {
    public static void main(String[] args) {
        final int x;
        try {
            throw new RuntimeException();
            x = 1;
        } finally {}
        x = 2;
        System.out.println("x: " + x);
    }
}

我希望它能打印“x:2”。

A.java:6: unreachable statement
            x = 1;
            ^
A.java:8: variable x might already have been assigned
        x = 2;
        ^
2 errors

它说它不会编译,因为在第8行,x = 2可能重新分配最终变量,但这是假的,因为如上所述,行x = 1无法访问,因此它将首次分配,而不是重新分配。

为什么编译器在知道尚未分配x时会发出错误,指出“x可能已被分配”?

6 个答案:

答案 0 :(得分:2)

在JLS第16章

中对此进行了解释
  

[...]同样,每个空白的最终变量必须至多分配   一旦;在转让它时,它必须是绝对未分配的   发生。

     

这样的赋值被定义为当且仅当其中一个时发生   变量的简单名称(或者,对于字段,它的简单名称   由此限定)发生在作业的左侧   操作

     

对于空白最终变量的每个赋值,变量必须为   在赋值之前肯定是未分配的,或者是编译时错误   发生。

因此,JLS似乎并不关心无法访问的代码。

关于例外,它说:

  

catch子句(§14.20)的异常参数V肯定是   在身体之前分配(而且绝对不是未分配)   捕获条款。

因此问题在于x=1x=2都明确指定为

  

如果try语句确实有finally块,那么这些规则也是如此   适用:

V is definitely assigned after the try statement iff at least one of the following is true:

    V is definitely assigned after the try block and V is definitely assigned after every catch block in the try statement.

    V is definitely assigned after the finally block.

    V is definitely unassigned after a try statement iff V is definitely unassigned after the finally block.

答案 1 :(得分:0)

第一个错误是代码无法访问的结果。抛出异常后,该方法将暂停,并且永远不会执行下一行。您只需从x中删除最终修饰符即可修复第二个错误。但我必须问一下,为什么要编写一个程序,其唯一目的是抛出RuntimeException?

答案 2 :(得分:0)

java编译器不能像人类那样查看事物。它没有看到因果关系,只看错了。在这种情况下,它可能是一件好事,因为即使您修复了其中一个错误,另一个也会持续存在。

答案 3 :(得分:0)

这可能是一个语言定义问题。该规范的一个区域禁止重新分配,如果它已被分配。另一个区域讨论了无法访问的代码。

两者的结合可能从未真正解决过。

你能否提供一些更能代表你真正想要完成的事情的东西?

答案 4 :(得分:0)

看起来你正在学习Java。第一个错误是因为编译器可以看到在抛出异常后无法继续执行块:控制将跳转到捕获异常的任何位置。其他海报解释了第二个错误。

答案 5 :(得分:0)

我见过其他不太直白的案例,例如:

final int x;
boolean b = ...;
if(b) {
   x = 1;
}
...
if(!b) {
   x = 2;
}

最简单的解决方案是分配一个临时的非最终变量,然后将其复制到最终变量:

final int x;
int _x = 0;
boolean b = ...;
if(b) {
   _x = 1;
}
...
if(!b) {
   _x = 2;
}
x = _x;