为什么类不能扩展其中出现的静态嵌套类?

这个class:

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会“依赖”它本身,因为( 根据Java语言规范 ,Java SE 8版的§8.1.4“超类和子类” )类直接依赖于“[]中提到的任何类型的extendsimplements子句[…]作为超类或超界面名称的完全限定forms的限定符。“

但我真的不明白这里的动机。 什么是有问题的依赖? 它只是为了与InnerParent是非static的情况保持一致(并因此最终得到一个词法封闭的自身实例)?

这似乎是一个相当邪恶的角落,因为有许多与循环inheritance相关的错误 ,经常导致编译器中的无限循环,堆栈溢出和OOM。 以下是一些可能提供一些见解的相关报价:

错误4326631 :

这个例子是合法的,这在即将出版的第二版Java语言规范中已经明确了。 同时通过inheritance和封闭相关的类是有问题的,但是原始的内部类白皮书没有充分解决该问题,1.3之前的编译器也没有实现一致的策略。 在JLS第2版中,反向循环inheritance的规则已经扩展到禁止类或接口直接或间接地“依赖”它自身。 类型不仅取决于它扩展或实现的类型,还取决于在这些类型的名称中用作限定符的类型。

错误6695838 :

这两个类声明确实是循环的; 因此,对于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依赖于自身; 因此,应该以编译时错误拒绝代码。

错误8041994 :

退一步,在JLS2中引入了类和接口的直接依赖关系,以阐明JLS1并涵盖作为嵌套类的超类/超接口(例如,描述中的AB)。

错误6660289 :

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

1)类Outer
1a)外部的归因触发了外部类型变量的归属
2)Outer.T的归因
2a)Outer.T的归因触发其声明边界的归属
3)类Outer.Inner的归属
3a)Outer.Inner的归因触发Outer.Inner的类型变量的归属
4)Outer.Inner 的归因
4a)Outer.Inner.S的归因触发了其声明边界的归属
5)Outer.T的归因 – 这只会返回T的类型; 正如你所看到的,在这个阶段,尚未在代表T类型的对象上设置T的界限。

稍后,对于每个属性类型变量,javac执行检查以确保给定类型变量的边界不引入循环inheritance。 但我们已经看到Outer.T没有设定界限; 因为这是javac在尝试检测由Outer.Inner.S的声明边界引起的inheritance树中的循环时与NPE崩溃的原因。

错误6663588 :

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

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

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

受过良好教育的SWAG:因为JVM必须首先加载父类,其中包括加载内部类的命令。 在定义外部类之后 ,内部类由CL定义,因此对外部类的字段或方法的任何引用都是可解析的。 通过尝试通过内部扩展外部,它要求JVM在外部之前编译内部,从而产生鸡和蛋的问题。 问题的根源在于内部类可以引用其外部类的字段值,只要遵循范围和实例化(静态对非静态)的规则。 由于这种可能性,需要保证JVM在内部类中的任何时候都不会尝试访问或改变外部类中的任何字段或对象引用。 它只能通过编译两个类来解决这个问题,首先是外部,但是编译之前需要这些信息以确定不存在某种范围或实例问题。 所以它是一个捕获22。