【问题标题】:Why do I need to provide enclosing class object but not enclosed class object为什么我需要提供封闭类对象而不是封闭类对象
【发布时间】:2015-05-30 23:24:57
【问题描述】:
class OuterA {
    class InnerA {
    }
}
class SubclassC extends OuterA.InnerA { 
    SubclassC(OuterA outerRef) {
        outerRef.super(); 
    }
}
class XYZ {
    public static void main(String[] args) {
        new SubclassC(new OuterA());
    }
}

在上面的代码中

  1. 为什么我需要将OuterA 对象引用传递给SubclassC 构造函数才能编译.java 文件?
  2. 为什么 InnerA 对象引用不需要传递给 SubclassC 构造函数?

【问题讨论】:

  • 请注意,如果您的类InnerA 不需要绑定到OuterA 的实例,您可以声明它static,这个“问题”就会消失。从您的 cmets 看来,这可能会有所帮助。

标签: java inheritance inner-classes


【解决方案1】:

为什么我需要将 OuterA 对象引用传递给 SubclassC 构造函数才能编译 .java 文件?

因为SubclassC 扩展了 InnerA 类的定义。同时,InnerA绑定OuterA(即InnerA 的所有实例都将绑定到OuterA 的相应实例)。因此,要获取SubclassC的实例,就需要OuterA的实例。

为什么 InnerA 对象引用不需要传递给 SubclassC 构造函数?

因为在创建SubclassC 时,在底层首先会创建其超类的实例,即会有InnerA 的实例。您实际上可以将SubclassC 视为一种特殊 类型的InnerA

【讨论】:

  • 但是为什么语言不允许你有一个 SubclassC 的构造函数,你可以通过编写 new OuterA().new SubclassC() 来使用它,就像它允许你编写 new OuterA().new InnerA() 一样?我错过了什么?
  • 这是必需的,因为InnerA的定义。 InnerA(及其所有子类)总是需要 OuterA 来实例化。否则,(如果您不必提供OuterA 实例)您将断开OuterAInnerA 之间的链接(即您将忽略InnerA 嵌套在OuterA 中的事实)。如果InnerAstatic,则不需要
  • 但我确实提供了一个OuterA 实例。
  • @pbabcdefp, new OuterA().new SubclassC() 是不允许的,因为SubclassC 没有嵌套在OuterA 中。
  • @kocko 我知道从多态上讲,SubclassC 是 InnerA 的一个实例。所以我们有两个不同的实例,每个实例 InnerA 和 SubclassC。但是我们只创建了 1 个属于 SubclassC 的对象。我们在哪里创建了 InnerA 的不同对象(除了 InnerA 实例引用的 SubclassC 对象)?
【解决方案2】:
  1. 为什么我需要将 OuterA 对象引用传递给 SubclassC 构造函数才能编译 .java 文件?

因为InnerAOuterA 的内部类。这意味着OuterA.InnerA 类型的对象或其任何子类型(例如SubclassC)只能存在于封闭类(在本例中为OuterA)实例的上下文中。

这称为合格的超类构造函数调用。来自JLS

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

如果您不需要将SubclassC 实例链接到OuterA 的现有实例,您可以在OuterA 构造函数中创建新的OuterA 对象:

static class SubclassC extends OuterA.InnerA {
    SubclassC() {
        new OuterA().super();
    }
}
  1. 为什么不需要将 InnerA 对象引用传递给 SubclassC 构造函数?

由于SubclassC 扩展OuterA.InnerA当您执行outerRef.super() 时,您调用的是OuterA.InnerA 构造函数。要看到这种情况,请考虑以下代码:

public class Example {
    static class OuterA {
        OuterA() {
            System.out.println("Call OuterA constructor");
        }
        class InnerA {
            InnerA() {
                System.out.println("Call InnerA constructor");
            }

        }
    }

    static class SubclassC extends OuterA.InnerA {
        SubclassC(OuterA outerRef) {
            outerRef.super();
            System.out.println("Call SuperclassC constructor");
        }
    }

    public static void main(String[] args) {
        OuterA outerA = new OuterA();
        System.out.println("Before new SuperclassC()");
        new SubclassC(outerA);
    }
}

输出:

Call OuterA constructor
Before new SuperclassC()
Call InnerA constructor
Call SuperclassC constructor

【讨论】:

  • 您指出这实际上是在调用 InnerA ctor!
【解决方案3】:
1.Why do I need to pass OuterA object reference to SubclassC constructor for the .java file to compile? 

因为SubclassC扩展了类OuterA.InnerASubclassC extends OuterA.InnerA {

SubclassC(OuterA outerRef) {
        outerRef.super(); 

第二次

2. Why is InnerA object reference not required to be passed to SubclassC constructor?

因为 Innera 在 Outtera 内部,您在创建对象 new SubclassC(new OuterA()); 时调用它(即,您不需要创建单独的对象,因为 InnerAOuterA 内部。创建 OuterA 就足够了在这种情况下

【讨论】:

  • “因为你需要知道对象的名字”——对象没有名字。
  • 真的。将其更改为类实例的名称
  • 没有。实例对象并且没有名字。你不需要知道对象的名字,你需要有一个对象的引用。
【解决方案4】:

我们预计在构造函数运行之前会发生两件事:

  • 应该已经运行了基类(Object 如果extends 未使用)构造函数
  • 封闭的实例(如果有的话)应该已经完全构造好,并且在程序中会出现对它的引用

Java 通过在构造函数的开头自动放置super() 调用来强制执行第一条规则。请注意,没有任何东西作为 父实例:当您执行 new File() 时,您不需要提供 Object - 所以在构建 @ 时您不需要 InnerA 987654327@.

当一个非静态类在另一个类的词法范围内定义时,它可以访问封闭对象的字段:

class Outer {
  int val;
  class Inner {
    int val() {
      return val; // <-- we can access val, it's in scope
    }
  }
}

所以很明显我们需要一个指向它的链接。以下是通常创建链接的方式:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // baroque, I admit

同样的事情发生在扩展Inner的类的第一行:

class InnerExtended extends Outer.Inner {
  public InnerExtended(Outer outer) {
    outer.super(); // <-- calls Outer.Inner ctor
  }
}

即使不容易看出相似之处:

super();            // plain superclass ctor call
outer.new Inner();  // instantiation of inner class
outer.super();      // super() call in class extending inner

【讨论】:

  • 我刚刚找出了明显不一致的原因。如果InnerExtended 也嵌套在Outer 中,那么InnerExtended 的每个实例实际上都需要两个Outer 实例(Inner 的封闭实例和@987654338 的封闭实例@)。这些可以不一样!写入outer1.new InnerExtended(outer2) 将使outer1 成为InnerExtended 的封闭实例,outer2 成为其父项的封闭实例。当InnerExtended不嵌套在Outer中时,Outer只需要一个,但语法必须一致。
猜你喜欢
  • 1970-01-01
  • 2022-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多