3

I've checked Forward References During Field Initialization and this the answer from @assylias, but still I got no answer to the why.

Why a static block can assign the static variable declared after it but can NOT access it?

   class Parent {
        static {
            i = 2; // valid
            // can only assign new value to it instead of accessing it?
//            System.out.println(i); // invalid - compile-error
        }
        static int i = 0;
        static {
            i = 3; // valid
        }
    }

Is it due to the fact: the value is not initialized yet, so we just explicitly inhibit you from using it? or there are something related to security I don't know?


updated

this is not a duplicate of that problem which is about

Why this doesn't happen when accessing with class name?

This question is about why we have this design? for what purpose?

Hearen
  • 7,420
  • 4
  • 53
  • 63
  • there will be `compile error` as mentioned @Lino so you simply cannot get the bytecode – Hearen Mar 19 '19 at 06:55
  • 2
    @Lino This code doesn't compile. There is no bytecode to check. Don't post meaningless guesswork here. – user207421 Mar 19 '19 at 06:56
  • Yes, it is because it isn't initialized yet, which is what the error message says, isn't it? – user207421 Mar 19 '19 at 06:58
  • 1
    Possible duplicate of [Why illegal forward reference error not shown while using static variable with class name](https://stackoverflow.com/questions/29153703/why-illegal-forward-reference-error-not-shown-while-using-static-variable-with-c) – Lino Mar 19 '19 at 07:02
  • @Lino please check the update for `duplication` issue – Hearen Mar 19 '19 at 07:07
  • 2
    @Hearen Asking *why* language desicions were made are not easy-to-answer-questions. This requires quite in-depth knowledge from one of the java developers and unless Brian Goetz comes around I am not sure if anyone here can answer this – Lino Mar 19 '19 at 07:10
  • Thanks for the help. Sorry to post such question. But first, this is not a duplicate; second, I know this question is not simple (even as you said impossible question) and that's why add this message in the question when I start it: "There will be downvotes, but I just lost the clue in the forest." which is removed by luk2302. – Hearen Mar 19 '19 at 07:12
  • @Lino Thanks for the help along the way. My idea is simple: I want to learn it & understand it. Sorry for bothering you much :) have a nice day ;) – Hearen Mar 19 '19 at 07:17

3 Answers3

4

Static fields are initialized based on the order they appear in the code.

So, when you assign a value to i variable you just say to the compiler: "Hey, guy, when you get to initialize this variable, set its value to...". But you can not use it until it's initialized because it simply does not exist yet.

UPDATE:

As it says in the book "The Java Language Specification" by James Gosling, Bill Joy, Guy Steele and Gilad Bracha:

These restrictions are designed to catch, at compile time, circular or otherwise malformed initializations.

Consider this:

static {
            i = 2;
            j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;

Should j variable be 7 or 15? If it's 7, then we have initialized i variable twice, which is not possible, since the field is static. If it's 15 then what does i = 2; mean?

This code is ambiguous, so Java Specification does not allow to do that.

Pavel Smirnov
  • 4,611
  • 3
  • 18
  • 28
  • 1
    Provided some explanation. – Pavel Smirnov Mar 19 '19 at 07:40
  • Wow, thank you so much. I just got lost and have no idea why this is not allowed. This example does give me the clue. Thank you so much! Pavel – Hearen Mar 19 '19 at 08:53
  • 1
    “*then we have initialized `i` variable twice, which is not possible, since the field is static*” ahem, why do you think that this is not possible? You can assign a variable as often as you like, unless it is **`final`**. You can easily write `static int i = 2, j = i++ + 5; static { i += 3; }` Or you change `j = i + 5;` in your own example to `j = ClassName.i + 5;` and it works, as the variable *does* exist already. – Holger Mar 20 '19 at 12:59
  • 1
    It says so in Java specification: "At run time, the initializer is evaluated and the assignment performed exactly once, when the class is initialized". Here https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.3 and here https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2 About referencing a variable by its full name. In this case the compiler knows exactly that the class has already been initialized This question was discussed here: https://stackoverflow.com/questions/29153703/why-illegal-forward-reference-error-not-shown-while-using-static-variable-with-c – Pavel Smirnov Mar 20 '19 at 14:19
0

After some further reading, I think Pavel is not quite accurate in this point as @Holger pointed out in the comment.

we have initialized i variable twice, which is not possible, since the field is static.

As the 12.4.2. Detailed Initialization Procedure points out

For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation.

I suppose the initialization twice is okay to the class initializer itself as long as it's just once to the invoking clients.

But the demos Pavel provided still stands its position so I basically just reuse it here but with different explanation.

static {
       i = 2;
       j = i + 5; 
       // no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;

But when you use MyClass.i directly in j = MyClass.i + 5, the compiler would know it would then be okay as 8.3.3. Forward References During Field Initialization detailed with four conditions.

Specifically, it is a compile-time error if all of the following are true:

  1. The declaration of a class variable in a class or interface C appears textually after a use of the class variable;

  2. The use is a simple name in either a class variable initializer of C or a static initializer of C;

  3. The use is not on the left hand side of an assignment;

  4. C is the innermost class or interface enclosing the use.

And there is a detailed discussion in this answer already.

To wind it up, I think this would be for predictable behavior to add these restrictions. Once again, the other official purpose pointed out in 8.3.3. Forward References During Field Initialization.

These restrictions are designed to catch, at compile time, circular or otherwise malformed initializations.

Hearen
  • 7,420
  • 4
  • 53
  • 63
0

The same is also applicable to the non-static members. You can assign values to them in instance initializer block but can not use them before initialization.

class Parent {
    {
        x = 3; // works fine
        // System.out.println(x); // gives compilation error.

    }
    int x = 0;

    public static void main(String[] args) {

    }
}
Sumit
  • 121
  • 1
  • 9