Почему в этом случае разрешен проверенный тип исключения?
Я случайно заметил, что этот оператор throw
(извлеченный из более сложного кода) компилирует:
void foo() {
try {
} catch (Throwable t) {
throw t;
}
}
В течение короткого, но счастливого момента я подумал, что проверенные исключения, наконец, решили просто умереть уже, но это все еще вызывает возмущение:
void foo() {
try {
} catch (Throwable t) {
Throwable t1 = t;
throw t1;
}
}
Блок try
не должен быть пустым; кажется, что он может иметь код, пока этот код не выдает проверенное исключение. Это кажется разумным, но мой вопрос в том, какое правило в спецификации языка описывает это поведение? Насколько я могу судить, §14.18 Заявление о выбросе явно запрещает его, поскольку тип выражения t
является проверенным исключением, и он не пойман или не объявлен брошенным. (?)
Ответы
Ответ 1
Я думаю, что формулировка в §14.18 The throw
Заявление, что вы имеете в виду, это ошибка в JLS - текст, который должен быть обновлен с Java SE 7, и не было.
Бит текста JLS, описывающий предполагаемое поведение, приведен в п. 11.2.2. Исключение анализа выражений:
Оператор throw
чье выраженное выражение является окончательным или эффективным окончательным параметром исключения в предложении catch
, может вызывать класс исключений E iff:
- Е класс исключения, что
try
блок try
заявления, которое декларирует C может бросить; а также - E - присвоение, совместимое с любым классом исключаемых классов C; а также
- E не является присвоением, совместимым с любым из захватывающих классов исключений из предложений
catch
объявленных слева от C в том же самом заявлении try
.
Первая маркерная точка является релевантной; потому что параметр catch
-clause t
является окончательным (это означает, что он никогда не был назначен или не увеличивался или не уменьшался, см. §4.12.4 final
Variables), throw t
может только бросать то, что может быть выбрано блоком try
.
Но, как вы говорите, проверка времени компиляции в §14.18 не делает за это никаких санкций. §11.2.2 не определяет, что разрешено, а что нет; скорее, он должен быть анализом последствий различных ограничений на то, что можно бросить. (Этот анализ действительно возвращается к более нормативным частям спецификации - сам он использует его во второй точке маркера - но в §14.18 нельзя просто сказать "это ошибка времени компиляции, если она выдает исключение, которое она не может бросок за §11.2.2 ", потому что это будет круговой.)
Поэтому я считаю, что §14.18 необходимо скорректировать с учетом целей § 11.2.2.
Хорошая находка!
Ответ 2
Это связано с тем изменением, которое было включено в Project Coin, введенное в Java 7, чтобы обеспечить общую обработку исключений с повторным копированием исходного исключения. Вот пример, который работает в Java 7, но не в Java 6:
public static demoRethrow() throws IOException {
try {
throw new IOException("Error");
}
catch(Exception exception) {
/*
* Do some handling and then rethrow.
*/
throw exception;
}
}
Вы можете прочитать всю статью, объясняющую изменения здесь.
Ответ 3
Это поведение подробно описано в JLS в 11.2. Проверка времени исключения компиляции:
Оператор throw
чье выраженное выражение является окончательным или эффективным окончательным параметром исключения в предложении catch
, может вызывать класс исключений E iff:
-
Е класс исключения, что try
блок try
заявления, которое декларирует C может бросить; а также
-
E - присвоение, совместимое с любым классом исключаемых классов C; а также
-
E не является присвоением, совместимым с любым из захватывающих классов исключений из предложений catch
объявленных слева от C в том же самом заявлении try
.
(Акцент мой.)
Ваш второй пример завершился неудачей, поскольку t1
не является "параметром исключения для предложения catch
".