实例变量可以有效地最终化/在 Java 中只有局部变量有效地最终化吗?

Can instance variables be effectively final / Are only local variables effectively final in Java?

我想在 Java9 中使用 try-with-resources 增强功能,方法是将引用变量放入 try with resources 而不是整个变量声明中。我也知道要做到这一点,我必须遵循规则:Variable used as a try-with-resources resource should be final or effectively final。首先,我将尝试使用本地变量,然后使用实例变量。

  1. 局部变量:

-我将变量设置为 final,它遵循给定的规则并可以正常编译:

public static void main (String[] args) throws IOException{
        final FileWriter fw = new FileWriter ("test.txt");
        try(fw) {
            //some code
        }
    }

-如果我也删除了 final 关键字,它将再次编译,因为 fw 被认为是 effectively final -变量只初始化一次,从不改变。

public static void main (String[] args) throws IOException{
     FileWriter fw = new FileWriter ("test.txt");
    try(fw) {
        //some code
    }
}
  1. 实例变量:

但是同样的模式也适用于实例变量吗?试试吧。

-首先让实例变量final,它再次遵循规则并编译得很好:

public class Test {
  final FileWriter fw = new FileWriter ("a.txt");

    void m1() throws IOException {
        try(fw ) {
            //some code
        }
    }
}

-如果我删除 final 关键字,它应该再次编译,应该吗?因为我没有在任何地方改变 fw 而是只初始化一次 - 应该是 最终有效 。不幸的是,这行不通:

public class Test {
   FileWriter fileWriter = new FileWriter ("a.txt");

    void m1() throws IOException {
        try(fileWriter) {
            //some code
        }
    }
}

它给我消息:用作 try-with-resources 资源的变量应该是最终的或有效的最终。所以在这一切之后,我回到我的第一个问题。实例变量可以 有效地最终 还是该术语仅用于局部变量?正如我刚刚展示的那样,我从不改变变量(应该被视为 有效最终),但编译器从不威胁它。

Java Language Specification, section 4.12.4. final Variables,明确规定只有局部变量和参数才能有效final:

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.

  • A method, constructor, lambda, or exception parameter (§8.4.1, §8.8.1, §9.4, §15.27.1, §14.20) is treated, for the purpose of determining whether it is effectively final, as a local variable whose declarator has an initializer.

If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors. Conversely, a local variable or parameter that is declared final in a valid program becomes effectively final if the final modifier is removed.