JEP 216: Process Import Statements Correctly
Корректная обработка объявлений import
| Ответственный | Jan Lahoda |
| Тип | Feature |
| Область | Implementation |
| Статус | Closed / Delivered |
| Выпуск | 9 |
| Компонент | tools / javac |
| Обсуждение | compiler dash dev at openjdk dot java dot net |
| Трудоёмкость | S |
| Длительность | M |
| Связан с | JEP 215: Tiered Attribution for javac |
| Рецензенты | Alex Buckley, Brian Goetz, Jonathan Gibbons, Maurizio Cimadamore |
| Одобрен | Brian Goetz |
| Создан | 2014/08/26 13:05 |
| Обновлён | 2016/07/12 20:59 |
| Задача | 8056066 |
Аннотация
Исправить javac, чтобы он правильно принимал и отклонял программы независимо от порядка инструкций import и конструкций extends и implements.
Мотивация
В некоторых случаях javac принимает исходный код с одним порядком импортов и отклоняет тот же исходный код, в котором импорты просто переставлены (например, JDK-7177813). Это неправильно и сбивает с толку.
Описание
При компиляции классов javac проходит несколько этапов. Для обработки import важны два из них:
-
разрешение типов, при котором в переданном AST ищутся объявления классов и интерфейсов, и
-
разрешение членов, которое включает:
- (1a) если
T— класс верхнего уровня, обрабатываютсяimportв исходном файле, где определёнT, и импортированные члены добавляются в область видимостиT; - (1b) если
T— вложенный класс, выполняется разрешение класса, непосредственно объемлющегоT(если такой есть); - (2) выполняется проверка типов в конструкциях
extends/implementsклассаT; - (3) выполняется проверка типов для переменных типа
T.
- (1a) если
Эти этапы входят в процесс разрешения классов в 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.Outer, P.Outer.Nested и P.Q.I. Затем, если нужно проанализировать класс P.Outer, этап разрешения членов проходит так:
| 1. | Начинается разрешение P.Outer |
| 2. | Согласно 1a, начинается обработка import static P.Outer.Nested.*;, то есть ищутся члены P.Outer.Nested и его супертипов, в том числе транзитивных. |
| 3. | Начинается разрешение класса P.Outer.Nested (статические импорты могут импортировать и унаследованные типы) |
| 4. | Запускается разрешение P.Outer, но оно пропускается, так как уже выполняется |
| 5. | Выполняется проверка типов для I (конструкция implements), но I не удаётся разрешить, потому что его ещё нет в области видимости. |
| 6. | Начинается разрешение import P.Q.*, при котором все типы-члены P.Q (включая интерфейс I) импортируются в область видимости текущего файла |
| 7. | Разрешение 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 будет принимать программы, которые сейчас отклоняет, но не будет отклонять программы, которые сейчас принимает.