为什么我不能这样做/是否有解决方法来实现这一目标:
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中的功能。 / p>
我不想要的四种解决方法/解决方案,以及我不想要它们的原因:
“解决方案:将C放入A.”我不想这样,因为如果我不能修改A.java的代码(有应用程序有这个限制)怎么办?如果A是另一个API的一部分怎么办?然后我必须为C创建一个新文件,就像我在这里做的那样。
“解决方案:将C放在扩展A的D类中。”我不希望这样,因为C被限制为仅在类型D的实例上实例化。我想创建一个扩展B的类,可以在所有类型的实例上实例化(有需要这个的应用程序)。因此,我需要C不被其他类封闭,就像我在这里所做的那样。
(作为问题编辑添加 - 请参阅JoshuaTaylor对代码示例的回答)“解决方案:使B静态。”我不希望这样,因为如果B中的功能需要访问其封闭的A实例(有应用程序需要这个),该怎么办?因此,我需要B不是静态的,就像我在这里所做的那样。 (第二个问题编辑:您可以使B静态并使其构造函数接受其封闭的实例,将其保存在受保护的变量中以便在其子项中进行访问,但这不如RealSkeptic接受的答案那么优雅)
移除。请参见底部的编辑。
所以,如果你的回答表明我做了上述其中一项,那么不就是这个问题的答案,即使它可能对其他人有用。
如果您的答案是“这只是Java语言的一个缺陷,那么您只是无法完成这个概念性的想法”,这是一个好的答案,您应该发布它。但是只是一个警告:如果您错了,我会推迟将您的答案标记为已接受。如果这是您的答案,我将非常感谢您解释为什么对该语言的这种限制已经到位(因为这是该问题的标题)。
感谢您的帮助。
编辑:JoshuaTaylor的回答提出了一个有效的选项:你可以扩展B anonymously并避免编写构造函数,就像在RealSkeptic接受的答案中一样。我最初放弃了这个想法,因为它不允许你通过“A.this”访问C的封闭A实例。但是,我已经知道C没有A的封闭实例,除非它在A的定义中作为嵌套类特别定义。所以请注意:以下解决方案都没有允许您通过在C方法中写“A.this”来访问包含C的祖先B的A的封闭实例。类只能使用“.this”来访问它们的类型具体嵌套在。但是,如果B具有访问A的封闭实例的功能,则需要通过JoshuaTaylor方法的匿名类或通过RealSkeptic方法的任何其他类。答案 0 :(得分:11)
嗯,它可以完成,但你必须记住,每个构造函数都需要显式或隐式地调用它的超级构造函数。这就是为什么你得到“由于某些中间构造函数调用”错误,没有可用的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 Turner问:
如果你明确地将A传递给C的构造函数,那么C现在不能是静态的,并且在C中使用A作为“普通旧”成员变量来调用所需的方法吗?
应该注意的是,C既不是静态也不是内部类。它是一个单独的公共类,它扩展了一个内部类B.C的作者可能不知道B类的实现,所以它不知道使用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甚至如果它实际上没有使用它。 myA
和myC
之间的关联是myA
是关于myC
的直接封闭的B
实例。该术语取自section 8.1.3 of the JLS:
对于
S
的每个超类C
,它本身就是类或接口的直接内部类 {{1} ,有一个SO
的实例与SO
相关联,称为立即封闭的{{{ 1}}关于i
。当通过显式构造函数调用语句(第8.8.7.1节)调用超类构造函数时,确定对象的直接封闭实例(如果有),确定该类的直接超类的实例
此用法的官方参考
此用法称为限定的超类构造函数调用语句,在JLS, section 8.8.7.1 - Explicit Constructor Invocations中提及。
超类构造函数调用以关键字
i
开头 (可能以显式类型参数开头)或主 表达式或 ExpressionName 。它们用于调用构造函数 直接超类的。他们进一步分裂:
不合格的超类构造函数调用以 关键字
S
(可能以显式类型参数开头)。合格的超类构造函数调用以 Primary 开头 表达式或 ExpressionName 。它们允许子类构造函数 显式指定新创建的对象立即封闭 关于直接超类的实例 (§8.1.3)。 当超类是内部类时,这可能是必要的。
在该部分的末尾,您可以找到显式构造函数调用语句的示例,包括此用法。
答案 1 :(得分:3)
更新:您已经提到过您不想要第一个解决方案,但问题的措辞可能会导致人们 我希望内心阶级是静止的,所以我希望这对他们有用。对于您的确切问题,更恰当的答案在本答复的第二部分。
你可以,但是内部类必须是 static ,因为如果它不是,那么内部类的每个实例都有对外部类的封闭实例的引用。静态嵌套类没有该引用,您可以自由扩展它。
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