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

Why can't outer classes extend inner classes?

为什么我不能 this/is 有一个解决方法可以做到这一点:

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();
    }
}

两个编译错误是

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

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

坦率地说,我认为这个概念是合理的:我想做一个 B 的子class,这样当我为 A 做一个 B 时,我可以选择让它具有 B 中的功能或 C 中的功能。

想要的四个workarounds/solutions,以及为什么我不想要它们:

  1. "Solution: Put C inside of A." 我不想要这个因为如果我不能修改 A.java 的代码怎么办(有些应用程序有这个限制)?如果 A 是另一个 API 的一部分怎么办?然后我必须为 C 创建一个新文件,就像我在这里所做的那样。

  2. "Solution: Put C inside of a class D that extends A." 我不想要这个,因为这样 C 就被限制为只能在 D 类型的实例上实例化。我想制作一个 class 来扩展 B可以在类型 A 的 all 个实例上实例化(有些应用程序需要这个)。因此,我需要 C 不被另一个 class 包围,就像我在这里所做的那样。

  3. (作为问题编辑添加 - 请参阅 JoshuaTaylor 对代码示例的回答)"Solution: Make B static." 我不想要这个,因为如果 B 中的功能需要访问其封闭的 A 实例怎么办(有需要这个的应用程序)?因此,我需要 B 不是静态的,就像我在这里所做的那样。 (第二个问题编辑:您可以使 B 静态并让它的构造函数接收其封闭实例,将其保存在受保护的变量中以便在其 children 中访问,但这不如 RealSkeptic 接受的答案优雅)

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

因此,如果您的回答建议我执行上述操作之一,那么它不是这个问题的答案,即使它可能对其他人有用。

如果您的答案是 "This is just a flaw of the Java language, you simply can't accomplish that conceptual idea",那是一个不错的答案,您应该 post 它。不过只是一个警告:如果你错了,我会推迟将你的答案标记为已接受。如果这是你的答案,如果你能解释为什么对语言有这种限制(因为这是这个问题的标题),我将不胜感激。

感谢您的所有帮助。

编辑:JoshuaTaylor 的回答提出了一个有效选项:您可以扩展 B anonymously 并避免像 RealSkeptic 接受的答案那样编写构造函数。我最初放弃了这个想法,因为它不允许您通过 "A.this" 访问 C 的 A 封闭实例。但是,从那以后我了解到 C 没有 A 的封闭实例,除非它在 ​​A 的定义中明确定义为嵌套 class。所以请注意:none 下面的解决方案允许您通过在 C 的方法中写入 "A.this" 来访问包含 B 的 C 祖先的 A 的封闭实例。类 只能使用“ .this" 访问它们专门嵌套的类型。但是,如果 B 具有访问 A 的封闭实例的功能,则通过 JoshuaTaylor 的方法匿名 class 或通过 RealSkeptic 的方法任何其他 class 是必填。

您可以轻松扩展嵌套 static classes

Update: You've mentioned that you don't want this first solution, but the phrasing of the question may lead people to it who are willing to have the inner class be static, so I'll leave this in the hopes that it's useful to them. A more proper answer to your exact question is in the second section of this answer.

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

public class Outer {
    public static class Inner {

    }
}
public class InnerExtension extends Outer.Inner {

}

但你也可以扩展嵌套的非静态 classes

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
innerExt's foo: subclass foo

嗯,这可以做到,但你必须记住,每个构造函数都需要显式或隐式地调用其超级构造函数。这就是您收到 "No enclosing instance of type A is available due to some intermediate constructor invocation" 错误的原因。 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 Turner 问:

    If you are explicitly passing in an A to the constructor of C, can't C now be static, and have A as a "plain old" member variable in C on which you invoke the required methods?

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

  • @rajuGT 问:

    Is C is an individual entity? if so, why does it need A object? and what is the association between myA and myC in this case?

    是的,C 是一个单独的实体。它自己的任何方法都不需要 A。但是,如果它试图从 B 调用(或继承但不覆盖)涉及访问 A 的方法 - 那么 B 的实现需要 A。当然,形式上,B 的任何实例甚至都需要引用 A如果它实际上没有使用它。 myAmyC 之间的关联是 myA 相对于 B[=69= 的 myC 的直接封闭实例].该术语取自 section 8.1.3 of the JLS:

    For every superclass S of C which is itself a direct inner class of a class or interface SO, there is an instance of SO associated with i, known as the immediately enclosing instance of i with respect to S. The immediately enclosing instance of an object with respect to its class' direct superclass, if any, is determined when the superclass constructor is invoked via an explicit constructor invocation statement (§8.8.7.1)

此用法的官方参考

这种用法被称为限定超级class构造函数调用语句,并在JLS, section 8.8.7.1 - Explicit Constructor Invocations.

中提到

Superclass constructor invocations begin with either the keyword super (possibly prefaced with explicit type arguments) or a Primary expression or an ExpressionName. They are used to invoke a constructor of the direct superclass. They are further divided:

  • Unqualified superclass constructor invocations begin with the keyword super (possibly prefaced with explicit type arguments).

  • Qualified superclass constructor invocations begin with a Primary expression or an ExpressionName. They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (§8.1.3). This may be necessary when the superclass is an inner class.

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