Why attempt to print uninitialized variable does not always result in an error message

JavaInitializationFinal

Java Problem Overview


Some may find it similar to the SO question Will Java Final variables have default values? but that answer doesn't completely solve this, as that question doesn't directly print the value of x within instance initializer block.

The problem arises when I try to print x directly inside the instance initializer block, while having assigned a value to x before the end of the block :

Case 1

class HelloWorld {

    final int x;

    {
        System.out.println(x);
        x = 7;
        System.out.println(x);    
    }

    HelloWorld() {
        System.out.println("hi");
    }

    public static void main(String[] args) {
        HelloWorld t = new HelloWorld();
    }
}

This gives a compile time error stating that variable x might not have been initialized.

$ javac HelloWorld.java
HelloWorld.java:6: error: variable x might not have been initialized
        System.out.println(x);
                           ^
1 error

Case 2

Instead of directly printing, I am calling a function to print:

class HelloWorld {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    HelloWorld() {
        System.out.println("hi");
    }

    void printX() {
        System.out.println(x);
    }

    public static void main(String[] args) {
        HelloWorld t = new HelloWorld();
    }
}

This compiles correctly and gives output

0
7
hi

What is the conceptual difference between the two cases?

Java Solutions


Solution 1 - Java

In the JLS, §8.3.3. Forward References During Field Initialization, its stated that there's a compile-time error when:

> Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables > are in scope. Specifically, it is a compile-time error if all of the > following are true: > > - The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable; > > - The use is a simple name in either an instance variable initializer of C or an instance initializer of C; > > - The use is not on the left hand side of an assignment; > > - C is the innermost class or interface enclosing the use.

The following rules come with a few examples, of which the closest to yours is this one:

class Z {
    static int peek() { return j; }
    static int i = peek();
    static int j = 1;
}
class Test {
    public static void main(String[] args) {
        System.out.println(Z.i);
    }
}

Accesses [to static or instance variables] by methods are not checked in this way, so the code above produces output 0, because the variable initializer for i uses the class method peek() to access the value of the variable j before j has been initialized by its variable initializer, at which point it still has its default value (§4.12.5 Initial Values of Variables).

So, to summarize, your second example compiles and executes fine, because the compiler does not check if the x variable was already initialized when you invoke printX() and when printX() actually takes place at Runtime, the x variable will be assigned with its default value (0).

Solution 2 - Java

Reading the JLS, the answer appears to be in section 16.2.2: >A blank final member field V is definitely assigned (and moreover is not definitely unassigned) before the block (§14.2) that is the body of any method in the scope of V and before the declaration of any class declared within the scope of V.

This means that when a method is called, the final field is assigned to its default value 0 before invoking it, so when you reference it inside the method, it compiles successfully and prints the value 0.

However, when you access the field outside of a method, it is considered unassigned, hence the compilation error. The following code will also not compile:

public class Main {
	final int x;
	{
		method();
		System.out.println(x);
		x = 7;
	}
	void method() { }
	public static void main(String[] args) { }
}

because:

> - V is [un]assigned before any other statement S of the block iff V is [un]assigned after the statement immediately preceding S in the block.

Since the final field x is unassigned before the method invocation, it is still unassigned after it.

This note in the JLS is also relevant: >Note that there are no rules that would allow us to conclude that V is definitely unassigned before the block that is the body of any constructor, method, instance initializer, or static initializer declared in C. We can informally conclude that V is not definitely unassigned before the block that is the body of any constructor, method, instance initializer, or static initializer declared in C, but there is no need for such a rule to be stated explicitly.

Solution 3 - Java

The difference is that in the first case you are calling System.out.println from initializer block so the block which is invoked before constructor. In the first line

System.out.println(x);

variable x is not yet initialized so that you get compilation error.

But in the second case you call instance method which doesn't know if variable has already been initialized so you don't have compilation error and you can see the default value for x

Solution 4 - Java

Ok, here is my 2 cents.

We all know that final variables can be initialized only While declaring or later on in constructors. Keeping that fact in mind, let see what happened here so far.

No errors Case:

>So when you use inside a method, it have already a value.

 1) If you initialize it, that value.
 2) If not, the default value of data type. 

Error case : >When you do that in an initialization block, which you are seeing errors.

If you look at the docs of initialization block

{
    // whatever code is needed for initialization goes here
}

and

>The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.

In compiler's eye, your code is literally equals to

class HelloWorld {

    final int x;
    HelloWorld() {
        System.out.println(x);  ------------ ERROR here obviously
        x = 7;
        System.out.println(x);  
        System.out.println("hi");
    }

    public static void main(String[] args) {
        HelloWorld t = new HelloWorld();
    }
}

You are using it before even initializing it.

Solution 5 - Java

Case 1 :

Gives you a compile-error,

Because at System.out.println(x);

> you are trying to print x which was never initialized.

Case 2:

Works because you are not directly using any literal values, instead you are calling some method, which is correct.

General Rule is,

> If you are trying to access any variable which is never initialized > then it will give a compilation error.

Solution 6 - Java

We deal here with initializer block. The Java compiler copies initializer blocks into every constructor.

The compiler error don't occure in second example, because printing x is in another Frame, please refer to spec.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAshwani Kumar RahulView Question on Stackoverflow
Solution 1 - JavaKonstantin YovkovView Answer on Stackoverflow
Solution 2 - JavaTunakiView Answer on Stackoverflow
Solution 3 - Javak0nerView Answer on Stackoverflow
Solution 4 - JavaSuresh AttaView Answer on Stackoverflow
Solution 5 - JavaVishal GajeraView Answer on Stackoverflow
Solution 6 - JavaKrzysztof SzewczykView Answer on Stackoverflow