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

Some may find it similar to a SO question. Do Java Final variables complete default values? , but this answer does not completely solve this, since this question does not directly print the value of x inside the instance initializer block.

The problem occurs when I try to print x directly inside the instance initializer block by assigning the value x to 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 indicating that the variable x was not 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 direct printing, I call the 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 produces the result.

 0 7 hi 

What is the conceptual difference between the two cases?

+52
java initialization final
Nov 30 '15 at 9:35
source share
6 answers

In JLS, §8.3.3. Direct links during field initialization , it indicates that there is a compile-time error when:

The use of instance variables whose declarations appear text after use is sometimes limited, although these instance variables are in scope. In particular, this is a compile-time error if all of the following are true:

  • An instance variable declaration in a class or C interface appears after using an instance variable text

  • Usage is a simple name either in the initializer of the instance variable C, or in the initializer of the instance C;

  • Use is not on the left side of the task;

  • C is the innermost class or interface that spans usage.

The following rules are provided with a few examples that are closest to yours:

 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(Zi); } } 

Access to [static or instance variables] by methods is not checked in this way , therefore the code above outputs 0 , because the variable initializer for i uses the peek() class method to access the value of j before j been initialized the variable initializer, after which it still has a default value ( §4.12.5 Initial variable values).

So, to summarize your second example, it compiles and runs fine, because the compiler does not check if the variable x was initialized when printX() called, and when printX() is executed in Runtime, x will be given the default value ( 0 ).

+32
Nov 30 '15 at 10:01
source share

Reading JLS, the answer appears to be in section 16.2.2 :

The empty final member V field is definitely assigned (and, moreover, is not definitely not assigned) before the block (§14.2) that is the body of any method in the domain V and before the declaration any class declared in the framework V

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

However, when you access a field outside the method, it is considered unassigned, therefore, it is a 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 assigned [un] before any other S statement of the block iff V is [un] assigned after the statement immediately preceding S in the block.

Since the final field x not assigned before the method call, it is still not assigned after it.

This post in JLS is also relevant:

Note that there are no rules that would allow us to conclude that V definitely not assigned to a block that is the body of any constructor, method, instance initializer, or static initializer declared in C We can informally conclude that V not specifically assigned to a block, which is the body of any constructor, method, instance initializer, or static initializer declared in C, but such a rule does not need to be explicitly stated.

+11
Nov 30 '15 at 9:59
source share

Ok, here are my 2 cents.

We all know that final variables can only be initialized when declared or later in constructors. With this in mind, let's see what happened here.

No mistakes. Happening:

Therefore, when you use the inside of a method, it already matters.

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

Mistake:

When you do this in the initialization block, you see errors.

If you look at the docs of initialization block

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

and

The Java compiler copies initialization blocks to each constructor. Therefore, this approach can be used to share a block of code between multiple constructors.

In the eyes of the compiler, your code is literally equal

 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 use it before it is initialized.

+4
Nov 30 '15 at 9:56
source share

The difference is that in the first case, you call System.out.println from the initialization block, so the block that is called before the constructor. In the first line

 System.out.println(x); 

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

But in the second case, you call the instance method, which does not know if the variable was initialized, so you do not have a compilation error, and you can see the default value for x

+3
Nov 30 '15 at 9:50
source share

Case 1:

Gives you a compilation error,

Because in System.out.println(x);

You are trying to print an x ​​that has never been initialized.

Case 2:

It works because you are not directly using any literal values; instead, you are invoking some method that is correct.

General rule:

If you try to access any variable that is never initialized, it will give a compilation error.

+1
Nov 30 '15 at 9:44
source share

We are dealing with an initializer block. The Java compiler copies initialization blocks to each constructor.

A compiler error does not occur in the second example, since the print x is in a different frame, see the specification.

0
Nov 30 '15 at 10:03
source share



All Articles