2

I am puzzled why incrementing yet undeclared field (x++;) is not allowed inside an instance initializer, but it becomes allowed inside an instance initializer if wrapped into an anonymous class (yes, anonymous class has access to class fields, but the field is not initialized!).

class Test {

    { x++; }  // ERR: Cannot reference a field before it is defined

    Object anonFld = new Object() {     
        { x++; }     // fine! Sets x field below to 1 !

        void f() {
            x++; // fine!
        }
    };
    int x;

    // now x = 1     // anon constructor has set it!    

}
Sean Bright
  • 118,630
  • 17
  • 138
  • 146
Code Complete
  • 3,146
  • 1
  • 15
  • 38
  • @SeanBright nope, https://docs.oracle.com/javase/specs/jls/se12/html/jls-8.html#jls-8.6. Peruse earlier versions of the spec at your leisure. For example, [here it is in Java 6](https://docs.oracle.com/javase/specs/jls/se6/html/classes.html#246032). – Andy Turner Jul 11 '19 at 20:41
  • 3
    I believe you can also do it with `this.x++;` in an instance initializer of `Test`. This is all described in [JLS 8.3.3](https://docs.oracle.com/javase/specs/jls/se12/html/jls-8.html#jls-8.3.3). – Andy Turner Jul 11 '19 at 20:47

2 Answers2

6

The JLS, Section 8.3.3, gives the rules for giving a compiler error for a forward reference.

For a reference by simple name to an instance variable f declared in class C, it is a compile-time error if:

  • The reference appears either in an instance variable initializer of C or in an instance initializer of C (§8.6); and

  • The reference appears in the initializer of f's own declarator or at a point to the left of f's declarator; and

  • The reference is not on the left hand side of an assignment expression (§15.26); and

  • The innermost class enclosing the reference is C.

There is no provision for a compiler error for a reference from a nested or inner class. Curiously, a non-simple reference can gain access to a forward reference, e.g.

this.x++;

Also, later on in that same section, there is an example that explicitly states that access from a different class is ok.

class UseBeforeDeclaration {
    // Snipped
    {
      // Snippped
        j = j + 1;
      // error - right hand side reads before declaration
      // Snipped
      Object o = new Object() { 
          void foo(){ j++; }
            // ok - occurs in a different class
          { j = j + 1; }
            // ok - occurs in a different class
      };
  }
  // Snipped
  int j;
}

I've only included the relevant parts -- the instance initializer that makes a forward reference using an inner class, plus the declaration at the end.

Community
  • 1
  • 1
rgettman
  • 176,041
  • 30
  • 275
  • 357
  • 1
    You haven't actually explained *why* though, although this is of course a game of "guess what the language designers were thinking". I have always assumed it was a case of "a forward reference is *probably* a mistake; but we won't try especially hard to stop it". – Andy Turner Jul 11 '19 at 20:53
-4

The compiler compiles inner classes after the compilation of the "main" class. That's why the member x is available at the inner class while it's not available in the "main" class before its declaration.

Lothar
  • 5,323
  • 1
  • 11
  • 27