如何使外部类继承自内部类?

8

我该如何让类似这样的东西起作用:

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
  }
}

class OuterExtendsInner extends Outer.InnerBase {
  OuterExtendsInner(Outer o) { o.super(); }
  void method() {
    // How do I use some_member here?
    // Writing Outer.this.some_member -> error about Outer not being an enclosing class
    // Writing just some_member -> no-go, either
  }
}

解决方法是在InnerBase中编写一个返回Outer.this的方法,并从派生类中调用该方法,但是否还有其他方法?

我主要想从外部扩展InnerBase以实现更好的代码组织,但我可以将所有派生类移动到Outer中。


3
“更好的代码组织”很少涉及内部类,而我无法想象它会涉及类似这样的东西。 - Anon
这就是为什么我想将内部类扩展到外部类之外的原因。我正在实现一个解释器,每个“内部”类都模拟一种类型(名称、整数等)。该类型的行为可能需要或不需要对创建它的解释器对象的引用。我通过向可能需要它的每个方法传递解释器引用来解决了这个问题。 - zvrba
7个回答

3
您可以在 OuterExtendsInner 中实现此操作:
class OuterExtendsInner extends Outer.InnerBase {
    Outer o;

    OuterExtendsInner(Outer o) {
        o.super();
        this.o = o;
    }

    void method() {
        // now you can reference o.some_member
        int x = o.some_member;
    }
}

这就是我在问题中已经建议的(尽管是从InnerBase的一个方法返回Outer.this的引用)。 - zvrba
此外,在OuterExtendsInner中会有两个Outer.InnerBase的实例。一个是继承的,另一个是在构造函数中接收的。你不会谈论同一个“some_member”。 - Jérôme Verstrynge
1
@JVerstry:实际上,只有一个Outer.InnerBase实例从OuterExtendsInner扩展它。非静态的内部类不能在没有外部类实例的情况下构造。在这种情况下,传入的是Outer类实例,并且必须首先调用super()以使其正常工作。试一试,您可以通过向InnerBase添加构造函数并打印一行来验证。如果您调用new OuterExtendsInner(new Outer()),则只会有一个。 - WhiteFang34

3
这里的问题是将InnerBaseOuter链接的合成字段是私有字段。因此,我们只能从InnerBase内部访问外部对象,或者如果某些方法或字段提供对同一对象的引用。

1

我还没有尝试WhiteFang34的答案。它可能有效,但我不是很清楚...

如果你真的想在外部类之外定义内部类的扩展,最自然的方法是将其定义为另一个外部类中的内部类扩展你的外部类,如下所示:

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
  }
}

class OuterExtendsOuter extends Outer {
  class InnerExtendsInner extends Outer.InnerBase {
    void method() {
       System.out.println(some_member);
    }
  }
}

我实际上也没有运行过这段代码,但它应该可以工作。

更新:

根据评论线程,我现在已经编译并运行了我的代码和WhiteFang34的代码。

两者都能正常工作,但正如Paŭlo Ebermann在评论中指出的那样,两者都会创建两个外部类的副本。

我将为Paŭlo的答案点赞,并建议不要尝试使用任何一种方法来做这件事,因为这实际上是滥用内部类机制。

只需让您扩展的内部类位于同一个外部类中即可!

更新2:

根据运行时调试器的检查和对类的javap检查输出的检查结果,我的代码中发生的情况是InnerBaseOuterExtendsOuter$InnerExtendsInner都有名为this$0的合成私有最终字段。由于没有显式定义构造函数,因此使用默认构造函数,代码片段如下:

    OuterExtendsOuter outer = new OuterExtendsOuter();
    Outer.InnerBase inner = outer.new InnerExtendsInner();

导致这两个字段都引用outer

换句话说,Paŭlo的评论完全正确。

通过进一步的实验,如果您在Outer的另一个内部类中扩展InnerBase,则会发生相同的情况,因此它与它是否在同一个外部类中定义或其扩展无关,但实际上是非静态内部类通常处理的结果。

我怀疑这在某个地方有记录,但我没有看到过。

最好尽可能少地混合继承和内部类!


这个 InnerExtendsInner 对象有两个链接到封闭类的引用(一个是 InnerExtendsInner,另一个是 InnerBase),它们可能(例如通过默认构造函数)指向同一个对象。 - Paŭlo Ebermann
@Paŭlo Ebermann:我不明白你的意思。也许我需要尝试编译和运行代码... - Don Roby
我认为这会起作用,但实际上它与WhiteFang的答案类似,具有显式的“Outer”引用。(我应该做一个图片,但是在这个评论中我没有足够的空间。) - Paŭlo Ebermann

1

在 InnerBase 中只需要一个 getter 方法吗?

class Outer {
  int some_member;

  abstract class InnerBase {
    abstract void method();
    protected int getSome_Member() // This is possible, because Abstract classes can have non-abstract methods.
    {
       return some_member;
    }
  }
}

class OuterExtendsInner extends Outer.InnerBase {
  OuterExtendsInner(Outer o) { o.super(); }
  void method() {
       // you can access "some_member" now
      int myNumber = getSome_Member();

  }
}

1
答案是:你不能,因为它会破坏封装性。只有 InnerBase 可以访问 Outer 的属性,而不是 OuterExtendsInner。这不是直接的继承。InnerBase 不会继承 Outer 的属性。

你可以这样做,因为Outer的属性并不是InnerBase的属性,它们在语法上只是看起来像是(至少以前是这样 - 我无法想象会有所改变)。此外,这个问题并不一定涉及对Outer私有属性的直接访问。 - Robin Green
但是 OuterExtendsInner 继承自 InnerBase,因此它应该可以访问 InnerBase 可以访问的所有内容。 - zvrba
@Robin Green 请提供一个可操作的例子,但你会发现这是不可能的。OuterExtendsInner无法访问Outer的属性。 - Jérôme Verstrynge
@zvrba - 不对。当您说“它应该可以访问InnerBase可以访问的所有内容”时,这是错误的。如果InnerBase具有私有属性,则无法访问它们。只授予对继承的公共和受保护项的访问权限,而不是通过内部-外部关系(唯一的例外是内部可以访问它们的直接外部,但不是间接外部)。 - Jérôme Verstrynge

0
你的问题在于每个 InnerBase 实例(我知道它是抽象的)都必须有一个对 Outer 对象的引用。这是嵌套类语义的一部分。实例化 OuterExtendsInner 需要这样的引用。 你可以通过将 InnerBase 设为 static 嵌套类来避免这种情况。

这不是问题,问题在于如何获取对此“Outer”对象的引用。 - Paŭlo Ebermann

0

外部类可以扩展内部类,前提是内部类已编译为“.class”文件。

现在,每次编译外部类时都会遇到“extends innerclass”,但它尚未编译,编译器将抛出NoClassDefException或ClassNotFoundException异常。

是不是这样?所以你永远无法编译那个内部类。如果你能解决这个问题,你也可以扩展内部类:)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接