提问人:snickers10m 提问时间:10/9/2015 最后编辑:snickers10m 更新时间:10/9/2015 访问量:2994
为什么外部类不能扩展内部类?
Why can't outer classes extend inner classes?
问:
为什么我不能这样做/是否有解决方法可以做到这一点:
package myPackage;
public class A {
public class B {
}
}
package myPackage;
import myPackage.A.B;
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();
}
}
两个编译错误是
上
public class C extends B
No enclosing instance of type A is available due to some intermediate constructor invocation
上
C myC = myA.new C();
A.C cannot be resolved to a type
坦率地说,我认为这个概念的想法是合理的:我想做一个 B 的子类,这样当我为 A 做一个 B 时,我可以选择让它具有 B 中的功能或 C 中的功能。
我不想要的四种解决方法/解决方案,以及我不想要它们的原因:
“解决方案:将 C 放在 A 内部。”我不想要这个,因为如果我无法修改 A.java 的代码(有些应用程序有此限制)怎么办?如果 A 是另一个 API 的一部分,该怎么办?然后,我必须为 C 创建一个新文件,就像我在这里所做的那样。
“解决方案:将 C 放在扩展 A 的 D 类中。”我不希望这样,因为这样 C 被限制为只能在 D 类型的实例上实例化。我想创建一个扩展 B 的类,该类可以在 A 类型的所有实例上实例化(有些应用程序需要它)。因此,我需要 C 不被另一个类包围,就像我在这里所做的那样。
(添加为问题编辑 - 有关代码示例,请参阅 JoshuaTaylor 的回答)“解决方案:使 B 静态。”我不想要这个,因为如果 B 中的功能需要访问其封闭的 A 实例(有些应用程序需要这个)怎么办?因此,我需要 B 不是静态的,就像我在这里所做的那样。(第二个问题编辑:你可以让 B 成为静态的,并让它的构造函数接受它的封闭实例,将其保存在一个受保护的变量中,以便在它的子变量中访问,但这不如 RealSkeptic 接受的答案那么优雅)
删除。请参阅底部的编辑。
因此,如果您的回答表明我做了上述一项,那么它不是这个问题的答案,即使它可能对其他人有用。
如果你的答案是“这只是 Java 语言的一个缺陷,你根本无法实现这个概念性的想法”,这是一个不错的答案,你应该发布它。只是一个警告:如果你错了,我会推迟将你的答案标记为已接受。如果这是您的答案,如果您能解释为什么对语言有这种限制,我将不胜感激(因为这是这个问题的标题)。
感谢您的任何帮助。
编辑:JoshuaTaylor 的回答提出了一个有效的选项:您可以匿名扩展 B,并避免像 RealSkeptic 接受的答案那样编写构造函数。我最初放弃了这个想法,因为它不允许你通过“A.this”访问 C 的封闭实例 A。但是,我后来了解到 C 没有 A 的封闭实例,除非它在 A 的定义中被明确定义为嵌套类。因此,请注意:下面的解决方案都不允许你通过在 C 的方法中编写“A.this”来访问包含 C 的 B 祖先的封闭实例。但是,如果 B 具有访问 A 的封闭实例的功能,则需要通过 JoshuaTaylor 方法的匿名类或通过 RealSkeptic 方法的任何其他类。
答:
您可以轻松扩展嵌套的静态类
更新:你提到你不想要第一个解决方案,但这个问题的措辞可能会引导人们愿意让内部类是静态的,所以我会留下这个,希望它对他们有用。您的确切问题的更正确答案在本答案的第二部分。
您可以,但内部类必须是静态的,因为如果不是,则内部类的每个实例都具有对外部类的封闭实例的引用。静态嵌套类没有该引用,您可以自由扩展它。
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
评论
new ... () { ... }
嗯,这是可以做到的,但你必须记住,每个构造函数都需要显式或隐式地调用它的超级构造函数。这就是为什么您会收到“由于某些中间构造函数调用,没有可用的 A 型封闭实例”错误。的 no-args 构造函数试图隐式调用 的 no-args 构造函数,如果没有 .C
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 现在不能是静态的,并且将 A 作为 C 中的“普通旧”成员变量,并在其上调用所需的方法吗?
应该注意的是,C 既不是静态的,也不是内部类。它是一个单独的公共类,它扩展了一个内部类 B。类 B 的实现可能不为 C 的作者所知,因此它无法知道哪些方法将使用 A,也无法访问 A 的任何私有成员,因为 C 不是 A 的成员。但 B 需要,而 B 需要 A 实例。另一种方法是组合而不是继承(其中 C 持有一个 B 实例并将操作委托给它),但如果它想要创建该 B 实例而不是将其传递到内部,它仍然需要一个 A 实例,尽管它将使用 而不是 。
enclosing.new B
enclosing.super
@rajuGT问道:
C 是一个单独的实体吗?如果是这样,为什么需要 A 对象?在这种情况下,myA 和 myC 之间有什么关联?
是的,C 是一个单独的实体。它不需要 A 来使用它自己的任何方法。但是,如果它试图从 B 调用(或继承并且不覆盖)涉及访问 A 的方法,那么 B 的实现就需要 A。 当然,从形式上讲,B 的任何实例都需要对 A 的引用,即使它实际上没有使用它。和 are 之间的关联是
myC
相对于B
的直接封闭实例。该术语取自 JLS 的第 8.1.3 节:myA
myC
myA
对于
C
的每个超类 S,它本身是类或接口 SO 的直接内部类,都有一个与 i 关联的SO
实例,称为
i
相对于S
的紧邻实例。当通过显式构造函数调用语句 (§8.8.7.1) 调用超类构造函数时,对象相对于其类的直接超类的直接封闭实例(如果有)是确定的
此用法的官方参考
这种用法称为限定的超类构造函数调用语句,在 JLS 的第 8.8.7.1 节 - 显式构造函数调用中有所提及。
超类构造函数调用以关键字(可能以显式类型参数开头)或 Primary 表达式或 ExpressionName 开头。它们用于调用构造函数 的直接超类。它们进一步划分:
super
不合格的超类构造函数调用以 关键字(可能以显式类型参数开头)。
super
限定的超类构造函数调用以 Primary 表达式或 ExpressionName 开头。它们允许子类构造函数 显式指定新创建的对象的紧邻 关于直接超类的实例 (§8.1.3)。 当超类是内部类时,这可能是必要的。
在该部分的末尾,您可以找到显式构造函数调用语句的示例,包括此用法。
评论
public static class B