Definite Assignment Analysis for Final Variables
The name of a final variable can occur in the following contexts: in its declaration when it is declared, in the context of an assignment when it is assigned a value, and in the context where its value is accessed in an expression.
The analysis performed by the compiler determines whether the blank final variable is initialized before its value is accessed. This involves checking whether a blank final variable has been assigned a value on any possible path of execution to where the value of the variable is accessed. In technical terms, this means a blank final variable must be definitely assigned before any access.
final int k; // Declaration: blank final local variable
k = 10; // (1) Assignment
System.out.println(k); // (2) Access: k is definitely assigned.
boolean status = true; // Non-constant variable.
final int j; // Declaration: blank final local variable
if (status) { // (3) Value of conditional expression not evaluated
j = 5; // (4) Conditional assignment
}
System.out.println(j); // (5) Access: j is not definitely assigned!
The blank final local variable k is accessed at (2) after it has been assigned a value at (1). It is definitely assigned before access at (2). The situation is different at (5). The compiler can deduce that the blank final local variable j can be assigned a value in the if block, depending on the conditional expression in the if statement. In other words, the structure of the if statement is considered in the analysis, but not the non-constant conditional expression. The compiler cannot guarantee that the assignment at (4) will be executed. The conclusion being that the blank final local variable j is not definitely assigned before access at (5). The statement at (5) does not compile. However, if the conditional expression in the if statement at (3) is replaced with the boolean literal true, the compiler can determine that the assignment at (4) will be executed and the variable j at (5) becomes definitely assigned before access.
The flow analysis performed is conservative. The compiler does not always evaluate all expressions, particularly method calls. However, it does evaluate boolean-valued constant expressions, and it also takes into consideration the syntax of statements and expressions, including the use of the conditional operators !, &&, ||, and ? :.
For each assignment to a blank final variable, the compiler must also determine that it is assigned exactly once—in other words, it has not already been assigned a value. This means that, given an assignment to a blank final local variable, there should not be any other assignment to this variable on any possible path of execution prior to this assignment. In technical terms, this means a blank final variable must be definitely unassigned before any assignment.
int n = 5;
final int k; // Declaration: blank final local variable
if (n >= 4) { // (1) Value of conditional expression not evaluated
k = 6; // (2) Conditional assignment: k is definitely unassigned
}
k = 12; // (3) Assignment: k is not definitely unassigned!
System.out.println(k); // (4) Access: k is definitely assigned
The blank final local variable k in the assignment at (2) is definitely unassigned, as there is no other assignment on a path of execution before this assignment. However, the variable k at (3) is not definitely unassigned, as the assignment at (2) in the if statement can be executed on a path of execution before the assignment at (3). The compiler reports that the variable k at (3) may already have been assigned a value. The assignment at (3) does not compile. Note that the variable k at (4) is definitely assigned before the access, because of the assignment at (3). Removing the assignment statement at (3) makes the variable k at (4) not definitely assigned, as in the previous example.
If the code is modified by replacing the if statement at (3) with an if-else statement, where the assignment at (3) is done in the else block, the variable k at (3) also becomes definitely unassigned—there is no assignment to the variable k before this assignment. Regardless of which assignment executes in the if-else statement, the variable k at (4) is definitely assigned before access.
int n = 5;
final int k; // Declaration: blank final local variable
if (n >= 4) { // (1) Value of conditional expression not evaluated
k = 6; // (2) Conditional assignment: k is definitely unassigned
} else {
k = 12; // (3) Conditional assignment: k is definitely unassigned
}
System.out.println(k); // (4) Access: k is definitely assigned
The two rules of definitely assigned before an access and definitely unassigned before an assignment help to determine correct usage of blank final variables. For non-final local variables, the rule for definitely assigned before an access is sufficient, as the value of such variables can be changed.
The examples below shed more light on the usage of final variables.
{
final int i = 10;
i++; // Not OK. Side effect changes the value of i.
}
{
for (int i = 0; i < 10; i++) {
final int j = i; // OK. Final variable goes out of scope after each iteration.
}
}
{
final int j;
for (int i = 0; i < 10; i++) {
j = i; // Not OK. Loop either not executed and j not initialized
// or each iteration will assign a new value to j.
}
System.out.println(j); // Not OK. Not guaranteed that j is initialized.
}