Ajay
Ajay

Reputation: 7428

Instance Initializer vs private members

I have the following code :-

public class Test5 {
    private int value ;
    public static void main(String[] args) {
        Test5 a, b;
        a = new Test5();
        b = new Test5(){{ value = 1 ;}};
    }
}

The following line shows an error :-

b = new Test5(){{ value = 1 ;}};

non-static variable cannot be referenced from a static context.

The double-brace idiom states that the second brace is the instance initializer of the anonymous class. So why cannot it initialize a private member variable?

Upvotes: 4

Views: 343

Answers (3)

Nikolay Ivanov
Nikolay Ivanov

Reputation: 8935

Because you trying to set value value field which belongs to original Test5 class, and no instance of this class is available in static context. Consider following code

public class Test5 {
    private int value = 0;
    public static void main(String[] args) {
        (new Test5()).foo();
    }
    void foo(){
        Test5 b = new Test5(){

            {
                value = 1;
            }

        };
        System.out.println(value);
    }
}

output will be 1. If you want to set value of anonymous class you must reference it with this.value, but this will also give you a compile error because value is private.

Upvotes: 1

janko
janko

Reputation: 4203

The line

b = new Test5(){{ value = 1 ;}};

creates an instance of an anonymous class that extends Test5. However, because value is private, the anonymous class can not access the instance variable of its superclass.

As there is no variable called value visible to the anonymous subclass of Test5, the compiler looks for an alternative in the next scope. In this case, the next scope belongs to the static main method. The compiler discovers the instance variable of Test5, and it issues a warning because the instance variable can not be referenced from the static context.

You have two alternatives here:

  1. Either make the instance variable accessible to the anonymous class: protected int value;

  2. or make the variable accessible to the static main method: private static int value;

I take it from your question that the first alternative is what you really want to do.

@Tom: The problem is not that the static scope is searched first. If this were the case then alternative (1) would not work, because the instance variable value is still found first and can still not be referenced.

@Ken: Your instanceMethod() doesn't do, what you expect it to do! Have a look at the following code:

class Test5A {
    private int value;

    public void instanceMethod() {
        Test5A a = new Test5A() {{ value = 1; }}; // (A)
        System.out.println(this.value);
        System.out.println(a.value);
    }

    public static void main(String[] args) {
        new Test5A().instanceMethod();
    }
}

This example code mimics the behavior of your class. If you compile and execute it, you will see that the output is "1 0".

While the instance initializer of the anonymous subclass at (A) looks like it assigns a one to its own value instance variable, that variable is actually only visible in the superclass of the anonymous class. Instead, at the line (A) the only visible variable called value is the instance variable of the Test5A instance on which instanceMethod() is called. Therefore, it is changed to one.

Now let's increase the visibility of value:

class Test5B {
    protected int value;

    public void instanceMethod() {
        Test5B a = new Test5B() {{ value = 1; }};
        System.out.println(this.value);
        System.out.println(a.value);
    }

    public static void main(String[] args) {
        new Test5B().instanceMethod();
    }
}

This time the output is "0 1". The instance variable value is inherited by the anonymous subclass and it is visible to its instance initializer. Therefore, the one is assigned to the correct instance variable.

Upvotes: 5

Ken
Ken

Reputation: 814

The difference is creating an anonymous subclass within an instance's context versus a static context. Compare:

public class InnerClasses {
  int pack;
  private int priv;
  static private int stat;

  private class NotStatic {
    {
      pack = 1;
      priv = 1;
      stat = 1;
    }
  }

  private static class IsStatic {
    { 
      pack = 1;  // Static member class not tied to outer instance
      priv = 1;  // Ditto
      stat = 1;
    }
  }

  public void instanceMethod() {
    InnerClasses a = new InnerClasses() {
      {
        pack = 1;
        priv = 1;
        stat = 1;
      }
    };
  }

  public static void main(String[] args) {
    InnerClasses s = new InnerClasses() {
      {
        pack = 1;
        priv = 1;  // Anonymous subclass in static context
        stat = 1;
      }
    };
  }

}

The lines with comments do not compile. The ones for IsStatic are simple enough to understand, but the difference between the anonymous classes in instanceMethod and the static main is more subtle.

Note that private really has the effect of "visible only within the enclosing top-level class", and not "visible only within that class". (As I recall, the latter is the actual mechanism at the JVM level, and the way to get the former effect is by synthesizing accessor methods.) That's how NotStatic can access priv.

So apparently the difference is that when creating an anonymous subclass in a static context, it is not considered "enclosed". Someone more familiar with the JLS might be able to clarify.

Upvotes: 1

Related Questions