Greetings folks! Here I’m with yet another interesting situation in Java that I stumbled upon.

The situation is simple. I have two class level (static) blank final variables declared. As we all know, for the class to compile, we must initialize these variables in a static initializer block. Fair enough. Now, I will initialize the first variable the normal way. However, for the second one, I will use the class name as a qualifier. Everything seems fine. But when we try to compile the class, the second initialization whines!

Lets see some code for this.

public class StrangeStatic {
    private static final String name;

    private static final String email;

    static {
        // Compiles fine.
        name = "Adarsh";

        // Compilation Error: 
        // "The final field StrangeStatic.email cannot be assigned"
        StrangeStatic.email = "abcd@xyz.com";

        name = "";
    }
}

Listing 1: Java listing showing the problematic initialization.

This compilation error is the same as what we would get if we tried to re-assign an already assigned final static variable, as shown below.

public class StrangeStatic {
    private static final String one = "one";

    static {
        // Compilation Error: 
        // "The final field StrangeStatic.one cannot be assigned"
        one = "two";
    }
}

Listing 2: Final variable being re-assigned.

Explanation

I was highly intrigued by all this and decided to dig the truth up! So I opened up the Java Language Specification. The JLS classifies variable assignments into two categories — Definite Assignment and Definite Unassignment. (§16, JLS 3rd Edition).

Definite Assignment refers to a state wherein a local variable or a blank final field always has a value assigned to it before its value is actually accessed. Conversely, Definite Unassignment refers to the state before a blank final field is assigned for the first time, i.e. it must never be already assigned before an assignment is attempted for the first time.

In the discussion of Definitely Unassigned state, I observed a subtle clause in the JLS. Here I quote the same. Notice the line in bold type.

Similarly, every blank final variable must be assigned at most once;
it must be definitely unassigned when an assignment to it occurs. Such
an assignment is defined to occur if and only if either the simple
name of the variable, or its simple name qualified by this, occurs on
the left hand side of an assignment operator.
A Java compiler must
carry out a specific conservative flow analysis to make sure that, for
every assignment to a blank final variable, the variable is definitely
unassigned before the assignment; otherwise a compile-time error must
occur.

The key to note here is that JLS specifies that for a blank final field to be definitely assigned, it must be definitely unassigned first. Secondly, in the assignment line, simple name must be used. A simple name here refers to the variable name alone without any qualifiers.

Thus, if you observe this behaviour in your Java compiler, be rest assured that your compiler only adheres to the JLS. Additionally, §16.8 of the JLS gives further insights into this aspect of Java.

Comments


comments powered by Disqus