1

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.

Stefan
  • 969
  • 6
  • 9
  • 2
    Only *local variables* can be effectively final. – Andreas Nov 12 '20 at 19:01
  • 1
    I suspect it has to do with possible subclasses having access to it. If you make it private, does it still complain? – Gus Nov 12 '20 at 19:01
  • @Andreas That is what I was hoping to hear. Any doc to back up that comment, please? I couldn't find anything so far. – Stefan Nov 12 '20 at 19:01
  • 1
    @Gus Yep, it still complains. – Stefan Nov 12 '20 at 19:03
  • 1
    In Java, no access modifier implies default access, which mean ANY class in that package has access to that data member. Therefore, it is not effectively final simply because it is not modified from the declaring class itself. It can be modified outside the class. It is basically PUBLIC to the package. – hfontanez Nov 12 '20 at 19:03
  • It still complains because it is not local. Therefore, it MUST be declared as `final`. Not needed if the variable is declared locally. – hfontanez Nov 12 '20 at 19:05
  • 1
    @hfontanez Makes total sense. So my answer is that only local variables are effectively final as Andreas said? – Stefan Nov 12 '20 at 19:07
  • 1
    "Variable used as a try-with-resources resource should be final (for global fields) or effectively final (for local)" – hfontanez Nov 12 '20 at 19:08

1 Answers1

3

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.

Andreas
  • 154,647
  • 11
  • 152
  • 247