看看这个简单的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=1
和x=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;