为什么外部类不能扩展内部类? [英] Why can't outer classes extend inner classes?

查看:174
本文介绍了为什么外部类不能扩展内部类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  package myPackage; 

公共等级A {
公共等级B {

}
}






  package myPackage; 

import myPackage.A.B;

公共等级C延伸B {

}



< hr>

  package myPackage; 

公共类Main {
public static void main(String [] args){
A myA = new A();
C myC = myA.new C();
}
}

两个编译错误是


  1. On 公共类C扩展B 没有封闭的类型实例A由于某些中间构造函数调用而可用


  2. On C myC = myA.new C() ; AC无法解析为


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



想要的四种解决方法/解决方案,以及我不想要它们的原因:


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


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


  3. (作为问题编辑添加 - 请参阅JoshuaTaylor的回答代码示例)解决方案:使B静态。我不希望这样,因为如果B中的功能需要访问其封闭的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的封闭实例的功能,则需要通过JoshuaTaylor方法的匿名类或通过RealSkeptic方法的任何其他类。

解决方案

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



所以你修复了你的 C 将成为:

 公共类C扩展B {
公共C(随附){
enclosing.super();
}
}

然后你可以创建一个新的 C 使用:

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

评论中的问题解答




  • @Andi Turner问:


    如果你明确地传递在A的构造函数中,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 even如果它实际上没有使用它。 myA myC 之间的关联是 myA 是关于 B 直接附上 myC 的实例。该术语取自第8.1节.3 of the JLS


    对于每个超类 S C ,它本身就是类或接口的直接内部类 SO ,有一个 SO 的实例与 i相关联 ,相对于 S 立即附上 i 的实例$ C> 的。当通过显式构造函数调用语句(第8.8.7.1节)调用超类构造函数时,确定对象的直接封闭实例(如果有的话)





此用法的官方参考



此用法称为限定的超类构造函数调用语句,并在 JLS,第8.8.7.1节 - 显式构造函数调用


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




  • 不合格的超类构造函数调用以
    关键字 super <开头/ code>(可能以显式类型参数开头)。


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



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


Why can't I do this/is there a workaround to accomplish this:

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

The two compilation errors are

  1. On public class C extends B, No enclosing instance of type A is available due to some intermediate constructor invocation

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

Frankly, I think the conceptual idea is sound: I want to make a subclass of B so that when I make a B for A, I have the option of making it have the functionality in B or the functionality in C.

Four workarounds/solutions that I don't want, and why I don't want them:

  1. "Solution: Put C inside of A." I don't want this because what if I can't modify the code for A.java (there are applications that have this restriction)? What if A is part of another API? Then I have to create a new file for C, as I've done here.

  2. "Solution: Put C inside of a class D that extends A." I don't want this because then C is restricted to only being instantiated on instances of type D. I want to make a class that extends B that can be instantiated on all instances of type A (there are applications that need this). Therefore, I need C to not be enclosed by another class, as I've done here.

  3. (Added as a question edit - see JoshuaTaylor's answer for a code sample) "Solution: Make B static." I don't want this because what if functionality in B needs to access its enclosing instance of A (there are applications that need this)? Therefore, I need B to not be static, as I've done here. (2nd question edit: You could make B static and have its constructor take in its enclosing instance, saving it in a protected variable for access in its children, but this is less elegant than the accepted answer by RealSkeptic)

  4. Removed. See edit at bottom.

So, if your answer suggests that I do one of the above, it is not an answer to this question, even though it might be useful for other people.

If your answer is "This is just a flaw of the Java language, you simply can't accomplish that conceptual idea", that is an okay answer, and you should post it. Just a warning though: I will hold off on marking your answer as accepted in case you are wrong. If this is your answer, I would highly appreciate if you have an explanation for why this restriction on the language is in place (as that's the title of this question).

Thank you for any and all help.

EDIT: JoshuaTaylor's answer brings up a valid option: you can extend B anonymously and avoid having to write a constructor as in RealSkeptic's accepted answer. I originally discarded this idea because it does not allow you to access C's enclosing instance of A via "A.this". However, I have since learned that C does not have an enclosing instance of A unless it is specifically defined within the definition of A as a nested class. So please note: none of the solutions below allow you to access the enclosing instance of A that encloses C's ancestor of B via writing "A.this" in a method of C. Classes can only use ".this" to access types which they are specifically nested in. However, if B has functionality that accesses the enclosing instance of A, either an anonymous class via JoshuaTaylor's method or any other class via RealSkeptic's method is required.

解决方案

Well, it can be done, but you have to remember that each constructor needs to call its super constructor, explicitly or implicitly. That's why you get the "No enclosing instance of type A is available due to some intermediate constructor invocation" error. C's no-args constructor is trying to implicitly call B's no-args constructor, and it can't do that without an A.

So you fix your C to be:

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

And then you can create a new C by using:

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

Answers to the questions in the comments

  • @Andi Turner asked:

    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?

    It should be noted that C is neither static nor an inner class. It is an individual public class which is extending an inner class B. The implementation of the class B may not be known to the author of C, so it cannot know what methods would be using A, nor does it have access to any private members of A, as C is not a member of A. But B does, and B requires the A instance. An alternative approach would be composition rather than inheritance (where C holds a B instance and delegates operations to it), but if it wants to create that B instance rather than have it passed inside, it will still need an A instance, although it will use enclosing.new B rather than enclosing.super.

  • @rajuGT asked:

    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?

    Yes, C is an individual entity. It wouldn't need A for any of its own methods. But if it tries to call (or inherits and doesn't override) methods from B that involve access to A - then that A is required by the implementation of B. Formally, of course, any instance of B requires a reference to A even if it doesn't actually make use of it. The association between myA and myC are is that myA is the immediate enclosing instance of myC with respect to B. This term is taken from 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)

Official reference for this usage

This usage is known as a qualified superclass constructor invocation statement, and is mentioned in the 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.

At the end of that section, you can find examples for explicit constructor invocation statements, including this usage.

这篇关于为什么外部类不能扩展内部类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆