17

我很难理解Java中的非静态嵌套类。考虑以下示例,它先打印“Inner”,然后打印“Child”。

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()语法的含义 - 为什么它调用内部构造函数?

我只见过一个普通的super(args)用于调用超类构造函数和super.method()调用超类版本的重写方法,但从未见过这种形式的东西instance.super()

4

5 回答 5

11

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

从这里引用:

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

  • 备用构造函数调用以关键字 this 开头(可能以显式类型参数开头)。它们用于调用同一类的备用构造函数。

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

  • 不合格的超类构造函数调用以关键字 super 开头(可能以显式类型参数开头)。

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

于 2010-05-14T02:32:49.350 回答
11

内部类(非静态子类)本质上是嵌套类(静态子类),具有返回其父对象的隐式链接。这是您上面的代码,而是使用静态嵌套类编写的:

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() 是做什么的。

于 2010-05-14T02:37:53.537 回答
6

为什么o.super()inChild最终会调用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()

于 2010-05-14T02:48:17.423 回答
2

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

所以这就是为什么我们有有趣的语法,比如instance.new Inner()instance.super()——对于问题的答案“但是 Inner?”的上下文。不是很明显。(在外部类的非静态方法中,您可以只说new Inner(),并且像往常一样缩写为this.new Inner()。)

于 2010-05-14T02:38:09.893 回答
0

永远不要忘记基本原则,在调用子类构造函数的过程中,无论内部/外部类如何,总是首先实例化父类。在您的场景中,当您扩展内部类并且您的内部类是父类的成员时,需要对其进行实例化,然后调用实际的内部类构造函数。

于 2010-05-14T05:36:57.187 回答