Java 为什么可以';类是否扩展了其中出现的静态嵌套类?

Java 为什么可以';类是否扩展了其中出现的静态嵌套类?,java,language-lawyer,inner-classes,Java,Language Lawyer,Inner Classes,这一类: public class OuterChild extends OuterChild.InnerParent { public static class InnerParent { } } 未能编译: $ javac OuterChild.java OuterChild.java:1: error: cyclic inheritance involving OuterChild public class OuterChild extends OuterChild.In

这一类:

public class OuterChild extends OuterChild.InnerParent {
    public static class InnerParent {
    }
}
未能编译:

$ javac OuterChild.java
OuterChild.java:1: error: cyclic inheritance involving OuterChild
public class OuterChild extends OuterChild.InnerParent {
       ^
1 error
因为
OuterChild
将“依赖”自身,因为(per)一个类直接依赖于任何类型,“在[its]
扩展
实现
子句[…]中提到的作为超类或超接口名的完全限定形式的限定符。”


但我真的不明白这里的动机。什么是有问题的依赖关系?这仅仅是为了与
InnerParent
是非
静态的
(因此会以其自身的一个词汇封闭实例结束)的情况保持一致吗?

一个受过教育的SWAG:因为JVM必须首先加载父类,其中包括加载内部类的命令。内部类在外部类定义之后由CL定义,因此对外部类的字段或方法的任何引用都是可解析的。通过尝试通过内部扩展外部,它要求JVM先编译内部,再编译外部,从而造成鸡和蛋的问题。问题的根源在于,如果遵循范围和实例化(静态和非静态)的规则,内部类可能会引用外部类的字段值。由于这种可能性,JVM需要保证内部类中的任何内容在任何时候都不会试图访问或修改外部类中的任何字段或对象引用。它只能先编译这两个类才能找到答案,但在编译之前需要这些信息,以确保不会出现某种范围或实例问题。因此这是一个第22条军规。

这似乎是一个相当邪恶的角落,因为存在与循环继承相关的问题,通常会导致编译器中的无限循环、堆栈溢出和OOM。以下是一些相关的引文,可以提供一些见解:

:

这个例子是不合法的,这一点在即将出版的 Java语言规范的第二版。同时上课 然而,由继承和封闭两方面联系在一起是有问题的 最初的InnerClass白皮书没有充分解决这个问题, 1.3之前的编译器也没有实现一致的策略。在JLS第二 版本中,禁止循环继承的规则已扩展为禁止 类或接口直接或间接地依赖于自身。 类型不仅取决于它扩展或实现的类型,还取决于 在这些类型的名称中用作限定符的类型

:

这两个类声明实际上是循环的;根据JLS 8.1.4,我们有:

Foo依赖于Foo$Intf(Foo$Intf出现在Foo的implements子句中)
Foo$Intf依赖于Moo$Intf(Moo$Intf出现在Foo$Intf的extends子句中)
Foo$Intf依赖于Foo(Foo作为限定符出现在Foo$Intf的extends子句中)

对于及物性,我们认为Foo依赖于自身;因此,代码应该被拒绝,并出现编译时错误

:

退一步说,在JLS2中引入了类和接口的直接依赖关系,以澄清JLS1,并涵盖嵌套类的超类/超接口(例如描述中的A.B)

:

这个问题是由于javac执行wrt类属性的类型变量边界属性的顺序造成的

1) 类外部属性
1a)外部触发器的属性外部类型变量的属性
2) 外部.T的属性
2a)外部.T的属性触发其声明绑定的属性
3) 类的外部属性。内部属性
3a)Outer.Inner的属性触发Outer.Inner类型变量的属性
4) 外部。内部的属性
4a)Outer.Inner.S的属性触发其声明绑定的属性
5) attribute of Outer.T-这只返回T的类型;如您所见,在这个阶段,还没有在表示T类型的对象上设置T的界限

稍后,对于每个属性化类型变量,javac执行检查以确保给定类型变量的绑定不会引入循环继承。但是我们已经看到,没有为外部设定界限;因此,当javac试图在继承树中检测由Outer.Inner.S的声明绑定引起的循环时,它会与NPE一起崩溃

:

类型变量边界可能引用属于循环继承树的类,循环继承树导致解析过程在查找符号时进入循环

对于您的特定问题“什么是有问题的依赖项?”来说,这似乎是一个复杂的编译时符号解析边缘情况,JLS2中引入的解决方案只是简单地禁止限定符类型以及实际超类型引入的循环


换句话说,理论上可以对编译器进行适当的改进,但在有人出现并实现这一点之前,更实际的做法是在语言规范中禁止这种不寻常的关系。

@downvoter:想解释一下原因吗?请注意,此问题发生在编译时,而不是运行时,因此,它与JVM的类初始化顺序无关。事实上,如果JVM确定它需要初始化一个静态的内部类,它就不需要首先初始化外部类-请参阅。谢谢!我现在已经通读了您发现的bug,虽然它们没有明确地说“很难,所以我们放弃了”,但我同意您的评估,那似乎是潜流-是的,我在JLS2文档/开发过程中寻找了一些解释更改原因的内容,但没有找到任何明确的内容。我想,如果对适当的邮件列表进行调查,就会发现类似这样的事情:“啊,让我们禁止它,然后了结它吧!”