实例初始化VS私有成员

问题描述:

我有以下代码: -实例初始化VS私有成员

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

下面的行显示了一个错误: -

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

非静态变量不能从静态上下文中引用。

双大括号表示第二个大括号是匿名类的实例初始值设定项。那么为什么它不能初始化私有成员变量?

线

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

创建扩展Test5匿名类的一个实例。但是,因为value是私有的,匿名类无法访问其超类的实例变量。

因为没有变量valueTest5的匿名子类可见,所以编译器会在下一个作用域中查找替代项。在这种情况下,下一个范围属于静态main方法。编译器发现实例变量Test5,并且它发出警告,因为实例变量不能从静态上下文中引用。

你这里有两种选择:

  1. 要么使匿名类的实例变量访问: 保护 int值;

  2. 或使变量访问静态main方法: 私人静态 int值;

我把它从你的问题的第一选择是你真正想做的事情。

@Tom:问题是而不是首先搜索静态范围。如果是这种情况,则替代方法(1)将不起作用,因为实例变量value仍然是第一个找到的,并且仍然不能被引用。

@Ken:你的instanceMethod()没有做,你期望它做什么!看看下面的代码:

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

这个例子代码模仿你的类的行为。如果您编译并执行它,您将看到输出为“1 0”。

尽管(A)中匿名子类的实例初始值设定项看起来像是为其自己的value实例变量赋值,但实际上该变量实际上只在匿名类的超类中可见。相反,在行(A)处,名为value的唯一可见变量是调用instanceMethod()Test5A实例的实例变量。因此,它被改为一个。

现在让我们增加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(); 
    } 
} 

这一次的输出为 “0 1”。实例变量value由匿名子类继承,它的对其实例初始值设定项可见。因此,它被分配给正确的实例变量。

+1

不,它实际上是可以访问的。 javac的报告错误是关于静态上下文,而不是访问修饰符。 – 2009-09-29 04:21:13

+0

汤姆是对的!值是可访问的......并且如前所述,其静态问题被报告为错误! – Ajay 2009-09-29 04:24:32

+2

如果我让价值“保护”而不是“私人”,那么Ajay的例子根本没有显示任何警告。也就是说,问题在于匿名类看不到变量,而编译器认为非静态变量是从main的上下文访问的。 – janko 2009-09-29 05:01:37

由于您试图设置属于原始Test5类的值字段,并且此类的任何实例在静态上下文中都不可用。考虑下面的代码

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); 
    } 
} 

输出为1.如果您想设置匿名类的价值,必须与THIS.VALUE引用它,但是这也会给你一个编译错误,因为值是私有的。

区别是在实例的上下文中创建一个匿名子类与静态上下文。比较:

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; 
     } 
    }; 
    } 

} 

带注释的行不能编译。 IsStatic的那些很容易理解,但instanceMethod中的匿名类与静态main之间的区别更加微妙。

请注意,private确实具有“仅在封闭*类别中可见”和“不在该类别中可见”的效果。 (我记得,后者是JVM级别的实际机制,并且获得前者效果的方式是通过综合访问器方法。)这就是NotStatic可以如何访问priv

所以显然不同的是,当在静态上下文中创建一个匿名子类时,它不被认为是“封闭的”。更熟悉JLS的人可能能够澄清。

+0

是的,你的匿名类在instanceMethod()可以参考priv实例变量。但是,它不是你可能期望它引用的变量。看看我的扩展答案。 – janko 2009-09-29 08:25:55