代码无法编译,因为最终变量"might"(但不能)分配两次?



看看这个简单的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可能已经赋值"?

在JLS第16章中进行了解释

[…]同样,每个空白的最终变量最多只能赋值一旦当分配给它时,它必须明确取消分配发生。

这样的赋值被定义为当且仅当变量的简单名称(或者,对于字段,它的简单名称限定于此)出现在任务的左侧操作人员

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

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

关于例外情况,它说:

catch子句(§14.20)的异常参数V肯定是在正文之前指定(而且不是绝对未指定)catch子句。

所以这里的问题是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.

第一个错误是无法访问的代码造成的。一旦抛出异常,该方法将被暂停,并且永远无法执行下一行。你可以通过简单地从x中删除最后一个修饰符来修复第二个错误。但我必须问,你为什么要写一个唯一目的是抛出RuntimeException的程序?

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

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

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

你能不能提供一些更能代表你真正想要实现的目标的东西?

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

我见过其他不那么简单的情况,例如:

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;

相关内容

最新更新