Reputation: 7428
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
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
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:
Either make the instance variable accessible to the anonymous class: protected int value;
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
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