2016-08-30 80 views
-3
public class A { 
    public A() { 
     System.out.println("A()"); 
    } 

    public class B { 
     public B() { 
      System.out.println("B()"); 
     } 
    } 
} 
class Caller extends A.B { 
    Caller(A a){ 
     a.super(); 
    } 
} 


public class Main { 
    public static void main(String[] args) { 
     Caller as= new Caller(new A()); 
    } 
} 

为什么我们需要在类中调用a.super()扩展内部类?它在做什么?继承自内部类

没有a.super()程序不想编译。

Error:(48, 20) java: an enclosing instance that contains A.B is required 
+4

扩展封闭类之外的*内*类是一个非常** **糟糕的代码设计。你应该重新考虑设计。 – Andreas

+0

编辑问题时不提及编辑问题本身也不太好。 – Scadge

+0

[此编辑](http://stackoverflow.com/revisions/39231197/5)的目的是添加一个不完整的'C'声明?它对这个问题毫无影响。 –

回答

3

答案是:因为这是在Java语言规范中指定的方式。

您的班级A.BA的内部班级。构造函数具有类型为A的隐藏参数 - 封闭(外部类)实例。

您已在您的班级Caller中划分了A.B,这本身并不是内部类。但是超类的构造函数需要这个隐藏的实例A - 外部类实例。

您在Java中传递此方法的方式是使用此a.super();语法。

Java语言规范定义这在section 8.8.7.1

合格超类构造调用与主 表达ExpressionName开始。它们允许一个子类的构造函数 明确指定新创建的对象的直接超类(§8.1.3)的直接封装 实例。当超类是内部类时,这可能是 。

-1

由于您Caller类扩展A.B,首先调用Caller构造品牌将是A.B构造函数(即使你没有看到它在你的代码)。因此a.super()不调用A.B构造函数。

+0

为什么a.super()什么都不做? –

+0

没有'a.super'程序不想编译。 –

+1

因为原来的问题改变总是很有趣,所以得到downvoted吧@Scadge? – Adam

-2

这不是a.super()构造函数调用A.B,而是Caller()构造函数隐式调用它,因为Caller extends A.B。只有在执行a.super()行之后。

+0

不是。 'Caller'扩展了'A.B',而不是'A',所以它的构造函数调用'A.B'的构造函数,而不是'A'。 'A.B'扩展了Object,所以它的构造函数不会调用A的构造函数。 'a.super()'实际上* does *调用'A.B'的构造函数。 –

+0

@JohnBollinger我并不是说'Caller()'调用''A的构造函数,请再次阅读我的答案。另外,最初的问题是“为什么'a.super()'调用'A.B'构造函数”,我的答案解释说它没有。 – Scadge

+2

@Scadge但它*确实*,这就是为什么你的答案是错误的。亚当罗西尼的回答也是。 –

5

a.super()不是调用A构造函数。构造函数A作为表达式new A()的结果在Main.main()中运行。

a.super()调用零元(只)B构造,指定a为的A含实例,其中,因为内部类A.B的一个子类中,每个Caller必须有一个参考。

+0

为什么'a.super()'调用nullary'B'构造函数? 对象'a'是'A'的一个实例,而不是'B'!它如何确定构造函数需要调用? –

+1

@NickNick:因为这就是这种奇怪的语法是如何工作的。 'a.super()'与'obj.method()'有着根本的区别。其中'obj.method()'表示在obj上调用''方法'','a.super()'意味着“调用正在构造的这个实例的超类构造函数,提供'a'作为外部类的引用。 (*应该*他们已经使用'x.y'语法?上帝不,但他们做到了。) –

0

错误说明了一切。内部类(非静态嵌套类)的关键之一是它们可以访问其外部(封闭)实例。但是内部类需要知道它属于哪个外部实例。这些信息保存在我们无法访问的Outer this$0引用中,但它仍然需要设置为某一点,并且这一点是构造函数的代码。

这就是为什么我们要通过创建outer.new Inner()内部类实例像

Outer outerInstance= new Outer(); 
Outer.Inner innerInstance = outerInstance.new Outer.Inner(); 
//       ^^^^^^^^^^^^^^^^^ 

感谢的是,外实例也传递给内部类的构造函数(如隐藏参数)。

现在既然要扩展内部类,这意味着你的类只需指定内部类,但这并不意味着它不再是Inner类型。

因此,因为Caller的实例也被认为是A.B内部类(因为它扩展了它),所以您必须确保它具有关于其外部实例(A类)的知识。要做到这一点,你在我们班的构造函数,你需要

  1. 有你的类的实例内超A.B
  2. 调用构造函数,所以你会让它保存this$0变量外部类的参考。

第一点是通过传递一个实例作为您的类构造函数的参数Caller(A a)来解决的。

第二点与调用outerInstance.new Inner()的方法类似,但这次我们不能使用new关键字,因为我们不想创建Inner类的新对象。我们想从超类构造函数调用代码来初始化当前的对象(及其隐藏的this$0字段正确)。通常的做法是致电super()。唯一可能有点奇怪的是,我们需要以某种方式让super()调用哪个外部实例包含一个实例。所以,很可能使其类似于outer.new Inner()语法,我们使用outer.super()语法,你的情况是

a.super();