2010-05-14 97 views
15

我很难包装我的头在Java中的非静态嵌套类。考虑下面的例子,它打印“Inner”和“Child”。Java:非静态嵌套类和instance.super()

class Outer { 
    class Inner { 
     Inner() { System.out.println("Inner"); } 
    } 
} 

public class Child extends Outer.Inner { 
    Child(Outer o) { 
     o.super(); 
     System.out.println("Child"); 
    } 
    public static void main(String args[]) { 
     new Child(new Outer()); 
    } 
} 

我知道Inner的实例总是必须与一个Outer实例关联,并且它也适用于Child,因为它扩展了Inner。我的问题是o.super()语法的含义 - 为什么它会调用Inner构造函数?

我只看到用来调用父类的构造纯super(args)super.method()调用覆盖方法的超类版本,但从未形式instance.super()的东西。

+0

大声笑...这是什么'面试 - 问题'标签? – Cristian 2010-05-14 04:26:41

+0

作为Java职位面试的一部分,我被要求完成IKM测验;这个问题是测验中的一个简化形式。 – Kiv 2010-05-14 12:34:09

+0

@Kiv你有没有现实生活中的例子? – chepseskaf 2013-10-07 13:14:10

回答

9

它被称为“合格的超类构造函数调用”。

here举例:

显式构造函数调用语句可以分为两种:

  • 可选的构造调用开始与这个关键字(可能显式类型参数的开头)。它们用于调用同一个类的替代构造函数。

  • 超类构造函数调用以super关键字(可能以显式类型参数开头)或主表达式开始。它们用于调用直接超类的构造函数。超类构造函数调用可以进一步细分:

  • 不合格的超类构造函数的调用与关键字super(可能带有明确的类型参数的开头)开始。

  • 合格的超类构造函数调用以一个主表达式开始。它们允许子类构造函数明确指定新创建的对象相对于直接超类的直接封闭实例(第8.1.3节)。当超类是内部类时,这可能是必要的。

9

内部类(非静态的子类)基本上嵌套类(静态子类)与隐性链接回到他们的父对象。这是你上面的代码,而不是使用一个静态嵌套类写着:

class Outer { 
    static class Inner { 
     final Outer outer; 
     Inner(Outer outer) { 
      this.outer = outer; 
      System.out.println("Inner"); 
     } 
    } 
} 

public class Child extends Outer.Inner { 
    Child(Outer o) { 
     super(o); // o.super(); 
     System.out.println("Child"); 
    } 

    public static void main(String args[]) { 
     new Child(new Outer()); 
    } 
}

看着这个,你应该能够理解o.super()在做什么。

+2

事实上,根据我的理解,非静态内部类本质上是上述的语法糖。 – 2010-05-14 02:42:48

+0

不,嵌套的静态类本质上是一个外层的类,这只是为了方便打包。 – 2010-05-14 02:51:12

+1

好吧,对。我的意思是一个*非静态的*内部类是语法糖顶部的语法糖:-D – 2010-05-14 02:55:21

2

从概念上讲,非静态内部类“属于”特定对象。它就像每个人都有自己的类的版本一样,就像属于特定对象的非静态字段或方法一样。

所以这就是为什么我们有有趣的语法像instance.new Inner()instance.super() - 为背景,其中问题的答案“但其Inner?”是不是很明显。 (在外部类的非静态方法,你就可以说new Inner(),和往常一样这对于短this.new Inner()

5

为什么o.super()Child最终调用Outer.Inner构造?这很简单:因为Child extends Outer.Inner和构造函数调用总是被链接在层次结构中。

这里有轻微扩张到您的片段来说明:

class Outer { 
    Outer() { 
     System.out.println("Outer"); 
    } 
    void outerMethod() { } 
    class Inner { 
     Inner() { 
      System.out.println("OuterInner"); 
      outerMethod();    
     } 
     String wealth; 
    } 
} 
class OuterChild extends Outer { 
    OuterChild() { 
     System.out.println("OuterChild"); 
    } 
} 
public class OuterInnerChild extends Outer.Inner { 
    OuterInnerChild(Outer o) { 
     o.super(); 
     System.out.println("OuterInnerChild"); 
     this.wealth = "ONE MILLION DOLLAR!!!"; 
    } 
    public static void main(String args[]) { 
     System.out.println(new OuterInnerChild(new Outer()).wealth); 
     new OuterChild(); 
    } 
} 

此打印:

Outer 
OuterInner 
OuterInnerChild 
ONE MILLION DOLLAR!!! 
Outer 
OuterChild 

一些主要意见:

  • 因为OuterInnerChild extends Outer.Inner,它继承wealth,就像正常的子类语义
    • 而且就像正常的子类语义的OuterInnerChild链构造函数的Outer.Inner
  • 因为OuterChild extends Outer构造,它的构造函数链,即使明确地不援引
    • 无论是或明或暗地中,构造函数链向上层次

但为什么编译器的需求是OuterInnerChild构造函数采用Outer o,并o.super()被调用?

现在专用于内部类的语义:它的工作要做,以确保OuterInnerChild所有实例已经一个封闭Outer实例Outer.Inner,超阶级的OuterInnerChild。否则,Outer.Inner的构造函数将不会有一个封闭实例Outer来调用outerMethod()

0

总是不要忘记基本原则,在调用子类构造函数的过程中,它总是首先实例化父类,而不考虑内部/外部类。在你的场景中,当你扩展内部类时,你的内部类是需要实例化的父类的成员,然后调用实际的内部类构造函数。