跳到主要内容

JEP 216:正确处理进口声明

概括

修复以正确接受和拒绝程序,无论语句和子句javac的顺序如何。import``extends``implements

动机

在某些情况下,javac将接受具有特定导入顺序的源代码,并拒绝仅重新排序导入的相同源代码(例如JDK-7177813)。这是错误且令人困惑的。

描述

javac编译类时使用多个阶段。考虑到import处理,两个重要的阶段是:

  • 类型解析,通过提供的 AST 查找类和接口声明,以及

  • 会员决议,包括:

    • ( 1a ) 如果T是 toplevel,import则源文件定义中的 sT将被处理,并将导入的成员添加到T的范围内
    • ( 1b ) 如果T是嵌套的,则解析直接包含的类T(如果有)
    • ( 2 ) extends/implements的子句T经过类型检查
    • ( 3 ) 的类型变量T进行类型检查

上述阶段是类_解析_javac过程的一部分,其中包括确定类的超类型、类型变量和成员。

要查看此过程的实际效果,请考虑以下代码:

package P;

import static P.Outer.Nested.*;
import P.Q.*;

public class Outer {
public static class Nested implements I {
}
}

package P.Q;
public interface I {
}

在类型解析阶段,认识到存在类型P.OuterP.Outer.NestedP.Q.I。然后,如果P.Outer要分析类,成员解析阶段的工作方式如下:

P.Outer启动分辨率

按照 1a处理开头,这意味着查找import static P.Outer.Nested.*;其成员及其传递超类型。P.Outer.Nested

类的解析P.Outer.Nested开始(静态导入也可以导入继承的类型)

触发 的解析P.Outer,由于该解析已经在进行中,因此被跳过

I(子句)的类型检查implements运行,但I无法解析,因为它不在范围内。

解析import P.Q.*开始,它获取所有成员类型P.Q(包括接口I)并将它们导入到当前文件的范围中

继续解决P.Outer其他类别的问题

如果导入被交换,则步骤 6 发生在步骤 5 之前,因此I在步骤 5 期间找到。

上述并不是与import处理相关的唯一问题。另一个已知问题是类的类型参数的边界可能有效地引用其声明类的可能的内部类。在某些情况下,这目前会导致无法解决的循环,例如:

package P;

import static P.Outer.Nested.*;

public class Outer {
public static class Nested<T extends I> {
static class I { }
}
}

这个问题的设想解决方案是将现有的第一阶段的javac成员解析分为三个:第一个阶段将分析封闭文件的导入,第二个阶段将仅构建类/接口层次结构,没有任何类型参数、注释_等。_,第三个将正确分析类头,包括类型参数。

预计此更改将允许javac接受当前被拒绝的计划,但不会拒绝当前已接受的计划。