Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instance initializer and *this* keyword [duplicate]

Tags:

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.

like image 358
Luigi Cortese Avatar asked Jun 19 '15 10:06

Luigi Cortese


People also ask

What is the use of this keyword inside the init block?

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 .

What is an instance initializer?

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.

What is difference between instance initializer block and constructor?

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.

Are instance variables automatically initialized?

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.


2 Answers

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.

like image 188
Zelldon Avatar answered Sep 19 '22 06:09

Zelldon


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.

like image 23
dejvuth Avatar answered Sep 21 '22 06:09

dejvuth