10

I was experimenting with anonymous classes today. When I do System.out.println(super.x);, it prints 12, and when I use System.out.println(x); it prints 4. I thought super.x would print 4 and was wondering if someone could please explain to me why this is?

public class AnonClass {

    private int x = 1;

    public AnonClass(int x) {
        this.x = x;
    }

    public static void main(String[] args) {
        AnonClass test = new AnonClass(4);
        test.testMethod();
    }

    public void testMethod() {

        AnonClass anon = new AnonClass(12) {
            {
                System.out.println(super.x); //Prints 12
                System.out.println(x); //prints 4
            }
        };

    }
}
Pshemo
  • 122,468
  • 25
  • 185
  • 269
Rubiks
  • 461
  • 1
  • 6
  • 21

2 Answers2

11

When you define an anonymous class like this inside class AnonClass:

AnonClass anon =
    new AnonClass(12) {
      {
        System.out.println(super.x); //Prints 12
        System.out.println(x); //prints 4
      }
    };

the compiler will create a class something like this:

class AnonClass$1 extends AnonClass {
  final AnonClass enclosed;

  AnonClass$1(AnonClass enclosed, int x) {
    super(x);
    System.out.println(super.x);
    System.out.println(enclosed.x);
    this.enclosed = enclosed;
  }
}

and then invoke it like:

AnonClass anon = new AnonClass$1(this, 12);

Note that the invocation of the superconstructor (super(x);) happens before the contents of the instance initializer (the System.out.println lines).

As such, the field AnonClass.x is initialized to 12 by the superconstructor, and then its value is printed as 12 via System.out.println(super.x);.

Then, System.out.println(x) is actually referencing the x in the enclosing instance of AnonClass, whose value is 4.

The reason it doesn't print 12 again is that x is private; and as it says in JLS Sec 8.2:

Members of a class that are declared private are not inherited by subclasses of that class.

So there is no AnonClass$1.x to print; the only identifier in scope called x is AnonClass.x.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 1
    Which part of the java specification says that an unqualified identifier in an anonymous class resolves to the enclosing class and not the anonymous super class? I would have expected an "ambiguous" compilation error. – Andreas Oct 11 '16 at 20:58
  • This is indeed an interesting question -- and one more reason to not use double brace initialization. A reference to the JLS regarding variable resolution in the instance initializer would be appreciated. – Mick Mnemonic Oct 11 '16 at 21:02
  • 2
    Here is a hint to answer my question, I guess: If you remove `private` from field `x`, both `super.x` and `x` refers to the super instance. Only when `x` is `private` will the unqualified `x` refer to the enclosing instance. – Andreas Oct 11 '16 at 21:08
  • That is interesting, it seems to add a level of encapsulation when made private and allowed to share when public. – Rubiks Oct 11 '16 at 23:53
1

You have two classes here: regular class AnonClass and anonymous class AnonClass$1 extending AnonClass

Both these classes have x field

You also have two objects: one of type AnonClass instantiated inside main method with x = 4 and second one of type AnonClass$1 instantiated inside testMethod() with x = 12

When you're printing the value of super.x you are accessing the x field of second object; but the value of x belongs to the first object cause you class is not static and contains an implicit reference to instance of outer class

Reference: Nested Classes in Java

bedrin
  • 4,458
  • 32
  • 53