Search code examples
javavariablesfinaltry-with-resourceseffectively-final

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


I want to use try-with-resources enhancement in Java 9 by putting reference variable inside try with resources instead of the entire variable declaration. I also know that to do that I must follow rule: Variable used as a try-with-resources resource should be final or effectively final. First I will try with local and then with instance variable.

  1. Local variable:

-I make variable final, which follows the given rule and compiles fine:

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

-If I also removed final keyword, it would again compile as fw is considered effectively final -variables that are initialized only once and never mutated.

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

But will this same pattern work for instance variable as well? Let's try.

-First let's make instance variable final, which again follows the rule and compiles fine:

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

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

-If I remove final keyword, it should again compile, should it? As I am not mutating fw anywhere but initializing it only once - should be effective final. Unfortunatelly, this won't work:

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

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

It gives me message: Variable used as a try-with-resources resource should be final or effectively final. So after all this, I am coming back to my first question. Can instance variable ever be effectively final or that term is used only for local variables? As I just showed, I am never mutating a variable (which should be considered effectively final), yet compiler never threats it as such.


Solution

  • The Java Language Specification, section 4.12.4. final Variables, clearly specifies that only local variables and parameters can be effectively 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.