Question

public class Main {

    static int x = Main.y;
//  static int x = y; //Not allowed; y is not defined
    static int y = x;
    public static void main(String[] args) {
        System.out.println(x);//prints 0
    }
}

How come I am allowed to use y trough the class, but not directly?

When is y defined?

Was it helpful?

Solution

The precise rules governing forward reference to class variables are described in the section §8.3.2.3 of the JLS:

8.3.2.3 Restrictions on the use of Fields during Initialization

The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively static) field of a class or interface C and all of the following conditions hold:

  • The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.
  • The usage is not on the left hand side of an assignment.
  • The usage is via a simple name.
  • C is the innermost class or interface enclosing the usage.

A compile-time error occurs if any of the four requirements above are not met.

This means that a compile-time error results from the test program:

  class Test {
      int i = j;  // compile-time error: incorrect forward reference
      int j = 1;
  }

whereas the following example compiles without error:

  class Test {
      Test() { k = 2; }
      int j = 1;
      int i = j;
      int k;
  }

even though the constructor (§8.8) for Test refers to the field k that is declared three lines later.

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

class Z {
  static int i = j + 2; 
  static int j = 4;
}

and:

class Z {
  static { i = j + 2; }
  static int i, j;
  static { j = 4; }
}

result in compile-time errors. Accesses by methods are not checked in this way, so:

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(Z.i);
  }
}

produces the output:

0

because the variable initializer for i uses the class method peek to access the value of the variable j before j has been initialized by its variable initializer, at which point it still has its default value (§4.12.5).

OTHER TIPS

I would assume that by using the class, the compiler would defer looking for the variable until the class was complete, so it finds y, but if you just define it like the comment it is not yet defined so it fails

Static variables are defined in order of declaration in the class, during class loading. When the JVM will load the Main class, x will be defined, then y. That's why you can't directly use y when initializing x, you create something that's called a forward reference, you refers to a variable not currently defined, and that's illegal for the compiler.

When using Main.y, I think the following happens:

  • You load Main, x initialization is called
  • When you define x to be equals to Main.y, the compiler sees a reference to a class, so it will end defining x to the current value of the member y of the class Main. It treats this case as if Main was a different class.

Note that in this case, when initializing x, y hasn't be defined for the moment. So x will have a value of 0.

You're not permitted to do it because it is meaningless. The only possible interpretation is that y is initialized to zero, and you already have two ways of saying that. You don't need this.

Perhaps the compiler creates the references of the static variables with default values with the class in the stack, when it's created and then assigns the provided values.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top