为什么 Java 中的局部变量不被视为 "effectively final",即使之后没有任何修改?

Why is a local variable in Java not considered "effectively final" even though nothing modifies it afterwards?

在一个方法中我有这个:

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 主体访问它。 doLater调用后没有修改x,所以x的值其实在调用doLater时就已经确定了。

我猜这个问题的答案是因为 x 没有资格被称为 effectively-final 变量。但是,我想知道是什么原因。

编译器不能只创建一个临时最终变量,有效地使代码像这样:

if (x != 0) {
    final int final_x = x;
    doLater(() -> showErrorMessage(final_x));
}

一切还是一样吗?

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

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

那么它就编译好了。

但是,添加条件可能改变变量的值

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

使变量不是有效的最终变量,从而增加编译错误。


此外,由于您实施的这种 pointer 方法行不通,您可以将代码稍微重构为一个简单的 if-else 语句:

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

并彻底摆脱 x

有效最终意味着它可以被制作final即它永远不会改变。这意味着实际上,变量可以是 final 一个。

问题是它不会跟踪您上次更改它的时间,而是跟踪您是否曾经更改过它。将您的 if 语句更改为

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

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

简短版本,如果一个变量被赋值一次,则它有效最终,无论执行哪个代码路径。

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

Certain variables that are not declared final are instead considered effectively final:

  • A local variable whose declarator has an initializer (§14.4.2) is effectively final if all of the following are true:
    • It is not declared final.
    • It never occurs as the left hand side in an assignment expression (§15.26). (Note that the local variable declarator containing the initializer is not an assignment expression.)
    • It never occurs as the operand of a prefix or postfix increment or decrement operator (§15.14, §15.15).

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

  • A local variable whose declarator lacks an initializer is effectively final if all of the following are true:
    • It is not declared final.
    • Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment; that is, it is definitely unassigned and not definitely assigned after the right hand side of the assignment expression (§16 (Definite Assignment)).
    • It never occurs as the operand of a prefix or postfix increment or decrement operator.