繁体   English   中英

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

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

这节课:

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 Edition的 8.1.4节“超类和子类” ),类直接依赖于[[其] extendsimplements提到的任何类型。子句[…]作为超类或超接口名称的完全限定形式的限定符。”

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

这似乎是一个非常有害的极端情况,因为存在许多与循环继承相关的错误,这些错误通常导致编译器中出现无限循环,堆栈溢出和OOM。 以下是一些可能提供一些见解的相关报价:

错误4326631

这个例子是合法的,在即将出版的第二版Java语言规范中对此做了明确说明。 通过继承和封闭性同时关联的类是有问题的,但是原始的内部类白皮书并未充分解决该问题,1.3之前的编译器也未实施一致的策略。 在JLS 2nd Edition中,扩展了禁止循环继承的规则,以禁止类或接口直接或间接“依赖”自身。 一个类型不仅取决于它扩展或实现的类型,还取决于那些类型名称中充当限定符的类型。

错误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 <T的归属扩展了Outer.Inner>
1a)外部属性触发外部属性类型变量的归属
2)Outer.T的归属
2a)Outer.T的归属触发其声明界限的归属
3)类Outer.Inner <S的归属扩展T>
3a)Outer.Inner的属性触发Outer.Inner的类型变量的属性
4)Outer.Inner <S>的归属
4a)Outer.Inner.S的归属触发其声明界限的归属
5)Outer.T的归因-除了返回T的类型外,什么都不做; 如您所见,在此阶段,尚未在表示T类型的对象上设置T的界限。

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

错误6663588

类型变量范围可能是指属于循环继承树的类,当查询符号时,此类会导致解析过程进入循环。

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

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

有教养的SWAG:因为JVM必须首先加载父类,其中包括加载内部类的命令。 内部类是在定义外部类之后由CL定义的,因此可以解析对外部类的字段或方法的所有引用。 通过尝试通过内部扩展外部,它要求JVM在外部之前编译内部,从而产生了鸡与蛋的问题。 该问题的根源在于,如果遵循围绕范围和实例化的规则(静态与非静态),则内部类可以引用其外部类的字段值。 由于这种可能性,因此需要确保JVM绝对不要让内部类中的任何内容尝试访问或变异外部类中的任何字段或对象引用。 它只能通过外部编译两个类来找出答案,但是编译之前需要此信息以确保不会出现某种范围或实例问题。 所以这是一个陷阱22。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM