为什么外部类不能扩展内部类?

为什么我不能这样做/是否有解决方法来完成此任务:

package myPackage; public class A { public class B { } } 

 package myPackage; import myPackage.AB; public class C extends B { } 

 package myPackage; public class Main { public static void main(String[] args) { A myA = new A(); C myC = myA.new C(); } } 

两个编译错误是

  1. public class C extends BNo enclosing instance of type A is available due to some intermediate constructor invocationNo enclosing instance of type A is available due to some intermediate constructor invocation

  2. C myC = myA.new C();AC cannot be resolved to a type

坦率地说,我认为概念是合理的:我想制作B的子类,这样当我为A制作B时,我可以选择使其具有B中的function或C中的function。

想要的四种解决方法/解决方案,以及为什么我不想要它们:

  1. “解决方案:把C放在A里面。” 我不想这样,因为如果我不能修改A.java的代码(有应用程序有这个限制)怎么办? 如果A是另一个API的一部分怎么办? 然后我必须为C创建一个新文件,就像我在这里所做的那样。

  2. “解决方案:将C放在扩展A的D类中。” 我不希望这样,因为C被限制为仅在类型D的实例上实例化。我想创建一个扩展B的类,可以在类型A的所有实例上实例化(有些应用程序需要这样)。 因此,我需要C不被其他类封闭,就像我在这里所做的那样。

  3. (作为问题编辑添加 – 请参阅JoshuaTaylor对代码示例的回答)“解决方案:使B静态。” 我不希望这样,因为如果B中的function需要访问其封闭的A实例(有应用程序需要这个),该怎么办? 因此,我需要B不是静态的,就像我在这里所做的那样。 (第二个问题编辑:你可以使B静态并使其构造函数接受其封闭的实例,将其保存在受保护的变量中以便在其子代中访问,但这不如RealSkeptic接受的答案优雅)

  4. 删除。 请参见底部的编辑。

所以,如果你的答案表明我做了上述其中一个,那么这不是这个问题的答案,即使它可能对其他人有用。

如果您的答案是“这只是Java语言的一个缺陷,那么您根本无法实现这个概念性的想法”,这是一个好的答案,您应该发布它。 但是只是一个警告:如果你错了,我会推迟你的答案。 如果这是您的答案,我将非常感谢您解释为什么对该语言的这种限制已经到位(因为这是该问题的标题)。

感谢您的帮助。

编辑:JoshuaTaylor的回答提出了一个有效的选项:你可以匿名扩展B,避免像在RealSkeptic接受的答案中那样编写构造函数。 我最初放弃了这个想法,因为它不允许你通过“A.this”访问C的封闭A实例。 但是,我已经知道C没有A的封闭实例,除非它在A的定义中被特别定义为嵌套类。 所以请注意:以下解决方案中没有一个允许您通过在C方法中编写“A.this”来访问包含C的祖先B的A的封闭实例。类只能使用“.this”来访问它们的类型具体嵌套在。但是,如果B具有访问A的封闭实例的function,则需要通过JoshuaTaylor方法的匿名类或通过RealSkeptic方法的任何其他类。

好吧,它可以完成,但你必须记住,每个构造函数都需要显式或隐式地调用它的超级构造函数。 这就是为什么你得到“由于某些中间构造函数调用而没有A类的封闭实例”错误。 C的no-args构造函数试图隐式调用B的无参数构造函数,如果没有A ,它就不能这样做。

所以你修复你的C是:

 public class C extends B { public C(A enclosing) { enclosing.super(); } } 

然后你可以使用以下方法创建一个新的C

 A myA = new A(); C myC = new C(myA); 

答案中的问题

  • @Andi特纳问道:

    如果你明确地将A传递给C的构造函数,那么C现在不能是静态的,并且在C中使用A作为“普通旧”成员变量来调用所需的方法吗?

    应该注意的是,C既不是静态的也不是内部的。 它是一个单独的公共类,它扩展了一个内部类B.C的作者可能不知道B类的实现,所以它不知道使用A的方法是什么,也不能访问任何私有成员A,因为C不是A的成员但是B确实如此,并且B需要A实例。 另一种方法是组合而不是inheritance(其中C持有B实例并将操作委托给它),但是如果它想要创建该B实例而不是将其传递到内部,它仍然需要一个A实例,尽管它会使用enclosing.new B而不是enclosing.super

  • @rajuGT问:

    C是个体吗? 如果是这样,为什么它需要A对象? 在这种情况下,myA和myC之间的联系是什么?

    是的,C是一个单独的实体。 对于任何自己的方法都不需要A. 但是如果它试图调用(或inheritance并且不覆盖)来自B的涉及访问A的方法 – 则B的实现需要A。当然,任何B的实例都需要引用A even如果它实际上没有使用它。 myAmyC之间的关联是myA关于BmyC直接封闭实例 。 该术语摘自JLS第8.1.3节 :

    对于C每个超类S本身是类或接口SO的直接内部类,存在与i相关联的SO的实例,称为关于S直接封闭的i实例 。 当通过显式构造函数调用语句调用超类构造函数时,确定对象的直接封闭实例(如果有的话)(§8.8.7.1)

官方参考此用法

这种用法称为限定的超类构造函数调用语句 ,在JLS的8.8.7.1节 – 显式构造函数调用中提到。

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

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

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

在该部分的末尾,您可以找到显式构造函数调用语句的示例,包括此用法。

您可以轻松扩展嵌套的静态

更新:你已经提到你不想要这个第一个解决方案,但问题的措辞可能会引导人们愿意让内部类是静态的,所以我会留下这个希望它是有用的给他们。 对于您的确切问题,更恰当的答案是在本答复的第二部分。

你可以,但内部类必须是静态的 ,因为如果不是,那么内部类的每个实例都有对外部类的封闭实例的引用。 静态嵌套类没有该引用,您可以自由扩展它。

 public class Outer { public static class Inner { } } 
 public class InnerExtension extends Outer.Inner { } 

但是您也可以扩展嵌套的非静态类

 package test; public class Outer { public class Inner { public String getFoo() { return "original foo"; } } } 
 package test; public class Extender { public static void main(String[] args) { // An instance of outer to work with Outer outer = new Outer(); // An instance of Outer.Inner Outer.Inner inner = outer.new Inner(); // An instance of an anonymous *subclass* of Outer.Inner Outer.Inner innerExt = outer.new Inner() { @Override public String getFoo() { return "subclass foo"; } }; System.out.println("inner's class: "+inner.getClass()); System.out.println("inner's foo: "+inner.getFoo()); System.out.println(); System.out.println("innerExt's class: "+innerExt.getClass()); System.out.println("innerExt's foo: "+innerExt.getFoo()); } } 
 inner's class: class test.Outer$Inner inner's foo: original foo innerExt's class: class test.Extender$1 innerExt's foo: subclass foo