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

Why can't a class extend a static nested class occurring within it?

这个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 本身会 "depend on",因为(根据 §8.1.4 "Superclasses and Subclasses" of The Java Language Specification, Java SE 8 Edition)一个 class 直接依赖于 "is mentioned in [its] extends or implements clause […] as a qualifier in the fully qualified form of a superclass or superinterface name."

的任何类型

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

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

这似乎是一个相当邪恶的极端情况,因为有一个 number of bugs 与循环继承相关,通常会导致无限循环、堆栈溢出和编译器中的 OOM。以下是一些可能提供一些见解的相关引述:

Bug 4326631:

This example is not legal, and this is made clear in the forthcoming 2nd edition of the Java Language Specification. Classes simultaneously related by both inheritance and enclosure are problematical, however the original innerclasses whitepaper did not adequately address the issue, nor did the pre-1.3 compilers implement a consistent policy. In JLS 2nd edition, the rule against cyclic inheritance has been extended to prohibit a class or interface from "depending" on itself, directly or indirectly. A type depends not only on types that it extends or implements, but also on types that serve as qualifiers within the names of those types.

Bug 6695838:

The two class declarations are indeed cyclic; accordingly to JLS 8.1.4 we have that:

Foo depends on Foo$Intf (Foo$Intf appears in the implements clause of Foo)
Foo$Intf depends on Moo$Intf (Moo$Intf appears in the extends clause of Foo$Intf)
Foo$Intf depends on Foo (Foo appears as a qualifier in the extends clause of Foo$Intf)

For transitivity, we have that Foo depends on itself; as such the code should be rejected with a compile-time error.

Bug 8041994:

Stepping back, the directly-depends relationship for classes and interfaces was introduced in JLS2 to clarify JLS1 and to cover superclasses/superinterfaces that are nested classes (e.g. A.B in the Description).

Bug 6660289:

This problem is due to the order in which javac perform attribution of type-variable bounds wrt class attribution.

1) Attribution of class Outer<T extends Outer.Inner>
1a) Attribution of Outer triggers attribution of Outer's type variable
2) Attribution of Outer.T
2a) Attribution of Outer.T triggers attribution of its declared bound
3) Attribution of class Outer.Inner<S extends T>
3a) Attribution of Outer.Inner triggers attribution of Outer.Inner's type variable
4) Attribution of Outer.Inner<S>
4a) Attribution of Outer.Inner.S triggers attribution of its declared bound
5) Attribution of Outer.T - this does nothing but returning the type of T; as you can see, at this stage T's bound has not been set yet on the object representing the type of T.

At a later point, for each attributed type variable, javac performs a check to ensure that the bound of a given type variable does not introduce cyclic inheritance. But we have seen that no bound is set for Outer.T; for this is the reason javac crashes with a NPE when trying to detect a cycle in the inheritance tree induced by the declared bound of Outer.Inner.S.

Bug 6663588:

Type-variable bounds might refer to classes belonging to a cyclic inheritance tree which causes the resolution process to enter a loop when looking up for symbols.

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

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