Why does my instance initializer block refer to a field before it is declared?

I understand that you cannot refer to a variable before its declaration and that all code (including instance initializers) that is inside the class body, but outside of any method, is executed in order before the constructor when the object (except for static variables and initializer blocks that start in order at the beginning of the program to initialize the entire class). Why then the following code compiles (and starts!):

 public class WhyIsThisOk { { a = 5; } // why is this ok??? int a = 10; public WhyIsThisOk() { } public static void main(String[] args) { WhyIsThisOk why = new WhyIsThisOk(); System.out.println(why.a); // 10 } } 
+5
source share
5 answers

From docs :

The Java compiler copies initialization blocks to each constructor. Therefore, this approach can be used to exchange a block of code between several designers.

The above statement is a bit misleading, because if we follow the explanation of the above document, we can rewrite the source code as follows:

 public class WrongVersionOfWhyIsThisOk { int a = 10; public WhyIsThisOk (){ a = 5; } public static void main(String[] args){ WrongVersionOfWhyIsThisOk why = new WrongVersionOfWhyIsThisOk (); System.out.println(why.a); } } 

But running WrongVersionOfWhyIsThisOk will produce 5 instead of 10, which the source code produces.

But in fact, this is both the initializer block and the variable assignment are copied to the constructor:

 public class RightVersionOfWhyIsThisOk { int a; public RightVersionOfWhyIsThisOk (){ a = 5; a = 10; } public static void main(String[] args){ RightVersionOfWhyIsThisOk why = new RightVersionOfWhyIsThisOk (); System.out.println(why.a); } } 

Update:

Here is a doc detailing the initialization order and constructor call:

4) Run the instance initializers and the instance variable initializers for this class, assigning the values ​​of the instance variable initializers to the corresponding instance variables, from left to right, in which they are displayed in text form in the source code for the class. If the execution of any of these initializers results in an exception, then no new initializers are processed, and this procedure terminates abruptly with the same exception. Otherwise, go to step 5.

5) Run the rest of the body of this constructor. If this execution completes abruptly, then this procedure terminates abruptly for the same reason. Otherwise, this procedure is performed normally.

+3
source

Instance initialization blocks are called at the time the instance is created. So it’s normal that after creating the why object it works.

Initialization order: 1) static block 2) Constructor 3) block instances in the order of appearance

+1
source

From docs :

8.3.2.3. Restrictions on the use of fields during initialization

The participant’s declaration must appear text before it is used only if the element is an instance (respectively static) field of class or interface C and all of the following conditions are true:

  • Use occurs in an instance (respectively static) initializer of a variable C or in an instance (respectively static) initializer
    from C.

  • Use is not on the left side of the quest.

  • Use is made using a simple name.

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

This is a compile-time error if any of the four requirements above does not occur.

In this case, the use is on the left side of the destination, so it is not a compile-time error.

+1
source

The contents of the initializer block are executed whenever any constructor is called (before the contents of the constructors).

Thus, you can provide a reference to any variables, since they will not be used unless the constructor is called otherwise. object created.

0
source

The order of the announcement does not matter. You can also write:

 public class WhyIsThisOk { { a = 5; } public WhyIsThisOk() { } public static void main(String[] args) { System.out.println(new WhyIsThisOk().a); } int a = 10; } 

It is important that the compiler copies (top to bottom) first a=5 , and then a=10 into the constructor, so that it looks like this:

 public WhyIsThisOk() { a = 5; a = 10; } 

Finally, look at this example:

 public class WhyIsThisOk { { a = get5(); } public WhyIsThisOk() { a = get7(); } public static void main(String[] args) { System.out.println(new WhyIsThisOk().a); } int a = get10(); public static int get5() { System.out.println("get5 from: " + new Exception().getStackTrace()[1].getMethodName()); return 5; } public static int get7() { System.out.println("get7 from: " + new Exception().getStackTrace()[1].getMethodName()); return 7; } public static int get10() { System.out.println("get10 from: " + new Exception().getStackTrace()[1].getMethodName()); return 10; } } 

Output:

 get5 from: <init> get10 from: <init> get7 from: <init> 7 
0
source

Source: https://habr.com/ru/post/1274644/


All Articles