2

Suppose I have the following piece of code

int myVar;
final boolean condition = <someCondition>;
if (condition) {
   myVar = 1;
}
if (condition) {
   System.out.println("myVar = " + myVar);
}

When I compiled this, I got the expected myVar might not have been initialized error. Is this a bug in the compiler? It's easy to see that "myVar" was set when condition is true, and it is only referenced when condition is true. (condition is also never reset)

P.S: To those comments on me needing to init it to 0, yes I am aware of that. But the point is, I wanted "myVar" to be final (ie., value set at most once)

One Two Three
  • 22,327
  • 24
  • 73
  • 114
  • 1
    @imk It's fairly obvious the OP already knows that, since the whole point of the question is about *why* the compiler is requiring the initialization. – azurefrog Oct 12 '17 at 17:20
  • 4
    The Java compiler simply does not do a deep enough analysis to conclude that a value is always assigned to variable `myVar` before it is used. In general, it does not attempt to correlate the conditions in different conditional statements. – John Bollinger Oct 12 '17 at 17:20
  • I don't think the compiler is aware that condition won't change. Take, for example, this being run on a CPU that is interrupted. Condition might change when it goes back to executing this. Never mind, I didn't look at the declaration of condition. What @JohnBollinger seems sufficient enough. – bhow Oct 12 '17 at 17:20
  • Intrigued I tried this specific scenario out and it compiled and runs with no problems using javac in jdk1.8.0_141. – d.j.brown Oct 12 '17 at 17:32
  • 1
    @d.j.brown That's because you assigned the compile-time constant `true` to `condition`. If you initialize it with a runtime value, it will fail. – erickson Oct 12 '17 at 17:37
  • @erickson yes that is true, now I see the issue in question. Interesting observation, but in practice is this likely to be worth enhancing the compiler for? I'd say no. – d.j.brown Oct 12 '17 at 17:40

1 Answers1

4

The initialization requirement is a formal part of Java, as described in the JLS:

For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.

(JLS 8, chapter 16; emphasis in the original)

The JLS goes on to say

The analysis takes into account the structure of statements and expressions; it also provides a special treatment of the expression operators !, &&, ||, and ? :, and of boolean-valued constant expressions.

Except for the special treatment of the conditional boolean operators &&, ||, and ? : and of boolean-valued constant expressions, the values of expressions are not taken into account in the flow analysis.

(emphasis added)

Note well that condition being final does not make it a "constant expression" as the specification defines that term. The specification goes on to give the specific rule for if statements:

V is [un]assigned after if (e) S iff V is [un]assigned after S and V is [un]assigned after e when [e evaluates to] false.

In your particular code, then:

int myVar;

myVar is definitely unassigned here.

final boolean condition = <someCondition>;
if (condition) {
   myVar = 1;
}

myVar is "assigned after S" because S, the body of the if statement, performs an unconditional assignment. myVar is not assigned after evaluation of the condition, however, whether the condition evaluates to true or false. Therefore, myVar is not definitely assigned at this point in the method.

if (condition) {

And nothing has changed at this point: myVar still is not definitely assigned as far as the JLS rules are concerned, so its value must not be read. The compiler is therefore obligated to report an error in the next statement:

   System.out.println("myVar = " + myVar);
}
John Bollinger
  • 160,171
  • 8
  • 81
  • 157