2016-10-11 104 views
10

我今天正在尝试匿名类。当我做System.out.println(super.x);,它打印12,当我用System.out.println(x);它打印4.我认为super.x将打印4,并想知道是否有人可以请向我解释为什么这是?匿名类变量

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

    } 
} 
+0

我想'super'只与继承使用。我猜不会。 – Gendarme

+2

@Gendarme你为什么猜这个?匿名类继承它们扩展的类。 –

+0

@AndyTurner嗯,我想我在Java中的知识太浅了。这整个事情超越了我。 – Gendarme

回答

11

当你定义内class AnonClass匿名类是这样的:

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

,编译器会创建一个类是这样的:

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

,然后调用它像:

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

请注意,superconstructor(super(x);)的调用发生在实例初始化程序(System.out.println行)的内容之前。

因此,字段AnonClass.x由superconstructor初始化为12,然后通过System.out.println(super.x);将其值打印为12

然后,System.out.println(x)实际上是在AnonClass封闭情况下,它的值是4

它不会再次打印12的原因是,xprivate引用x;正如它在JLS Sec 8.2中所述:

声明为private的类的成员不会被该类的子类继承。

所以没有AnonClass$1.x打印;在范围内称为x的唯一标识符是AnonClass.x

+1

java规范的哪个部分说,匿名类中的非限定标识符解析为封闭类而不是匿名超类?我会期待一个“模糊”的编译错误。 – Andreas

+0

这确实是一个有趣的问题 - 还有一个不使用双括号初始化的理由。将赞赏在实例初始化程序中引用JLS有关可变分辨率的内容。 –

+2

下面是一个提示,回答我的问题,我猜:如果从字段'x'中删除'private','super.x'和'x'都指向超级实例。只有当'x'是'private'时,不合格的'x'才会指向封闭的实例。 – Andreas

1

你这里有两类:普通类AnonClass和匿名类AnonClass$1延伸AnonClass

这两个类有x

你也有两个对象:实例化AnonClass类型的一个内部mainx = 4和第二种AnonClass$1内部示例testMethod()x = 12

当您打印super.x的值时,您正在访问第二个对象的x字段;但x值属于第一对象,因为你的类不static,并包含对外部类的实例

参考的隐式引用:Nested Classes in Java