Trying to compile this piece of code
public class Main { public static void main(String args[]) { new Main(); } { System.out.println(x); } //Error here int x=1; }
produces a cannot reference a field before it is defined
error. But if I change the initializer row to
{ System.out.println(this.x); }
it works like a charm, printing the default int value 0.
This is a bit confusing to me, why does this
makes the difference? Shouldn't it be redundant in this case? Can anyone explain me what happens behind the scenes to make it clear how it really works?
PS: I know that by declaring x
before the initializer would make it work too.
If the class contains a primary constructor, the secondary constructor must refer to it in its declaration. The declaration is done using this keyword. init block is used to initialise the member property skill .
Instance initializer block works are used to initialize the properties of an object. It is invoked before the constructor is invoked. It is invoked every time an object is created.
Initializer block : contains the code that is always executed whenever an instance is created. It is used to declare/initialize the common part of various constructors of a class. Constructors : are used to initialize the object's state.
Instance variables of numerical type (int, double, etc.) are automatically initialized to zero if you provide no other values; boolean variables are initialized to false; and char variables, to the Unicode character with code number zero. An instance variable can also be a variable of object type.
I will try it to explain on the compiler layer.
Say you have a method like:
int x; x = 1; System.out.println(x);
The compilation will succed and the execution as well. If you change the Method into this:
System.out.println(x); int x; x = 1;
It will not even compile the same it is with your given example.
The compiler copies the code of the { }
intializer into the ctor
and also the x=1
initialization.
As you said it works if you set the x=1
before the { }
intializer.
public class MainC { public static void main(String args[]) { new MainC(); } int x=1; { System.out.println(x); } }
See the following Java bytecode:
public MainC(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field x:I 9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 12: aload_0 13: getfield #2 // Field x:I 16: invokevirtual #4 // Method java/io/PrintStream.println:(I)V 19: return LineNumberTable: line 1: 0 line 7: 4 line 9: 9 line 10: 19
The field x
is declared and gets the value 1
before it is used in the System.out.println
call.
So why it doesn't work if you set it after the { }
from the same reason you cant use the Code of my second example. The field is declared after the usage which makes no sense.
So why it works with the this
keyword?!
Lets look us some code:
public class Main { public static void main(String args[]) { new Main(); } { System.out.println(this.x); } //Error here int x=1; }
The corresponding Java Bytecode for the ctor:
public Main(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 7: aload_0 8: getfield #3 // Field x:I 11: invokevirtual #4 // Method java/io/PrintStream.println:(I)V 14: aload_0 15: iconst_1 16: putfield #3 // Field x:I 19: return LineNumberTable: line 1: 0 line 7: 4 line 9: 14
So whats happens here? Easy speaking the this
keyword loads the Main object reference on the stack. After that the field x can be accessed so the System.out.println
call can be executed successfully.
JSL 8.6 should explain your compile-time error:
Instance initializers are permitted to refer to the current object via the keyword this (§15.8.3) ...
Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. See §8.3.3 for the precise rules governing forward reference to instance variables.
In §8.3.3, it says:
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.
That's why writing the simple name x
gives you the error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With