【问题标题】:Java: Non-static nested classes and instance.super()Java:非静态嵌套类和 instance.super()
【发布时间】:2010-05-14 02:19:19
【问题描述】:

我很难理解 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() 形式的东西。

【问题讨论】:

  • LOL...interview-questions 标签是什么?
  • 作为 Java 职位面试的一部分,我被要求完成一个 IKM 测验;这个问题是测验中的一个简化形式。
  • @Kiv 你有现实生活中使用的例子吗?

标签: java inner-classes


【解决方案1】:

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

引用here:

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 事实上,根据我的理解,非静态内部类本质上是上述的语法糖。
    • 不,嵌套的静态类本质上是一个外层类,只是为了方便打包。
    • 嗯,对。我只是说 non-static 内部类是语法糖之上的语法糖:-D
    【解决方案3】:

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

    这里稍微扩展一下你的 sn-p 来说明:

    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.Inner 的封闭Outer 实例,OuterInnerChild 的超类。否则,Outer.Inner 的构造函数将没有Outer 的封闭实例来调用outerMethod()

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 2015-09-15
          • 1970-01-01
          • 2010-09-09
          • 2017-06-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多