Почему законно повторно бросать Throwable в определенных случаях, не объявляя об этом?
Я ожидаю, что следующий код поднимет ошибку времени компиляции на throw t;
, потому что main
не объявляется throw Throwable
, но он успешно компилируется (в Java 1.7.0_45) и производит вывод вы ожидали бы, если бы эта ошибка компиляции была исправлена.
public class Test {
public static void main(String[] args) {
try {
throw new NullPointerException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
}
Он также компилируется, если Throwable
изменен на Exception
.
Это не скомпилируется, как ожидалось:
public class Test {
public static void main(String[] args) {
try {
throw new NullPointerException();
} catch(Throwable t) {
Throwable t2 = t;
System.out.println("Caught "+t2);
throw t2;
}
}
}
Это компилируется:
public class Test {
public static void main(String[] args) {
try {
throwsRuntimeException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
public static void throwsRuntimeException() {
throw new NullPointerException();
}
}
Это не означает:
public class Test {
public static void main(String[] args) {
try {
throwsCheckedException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
public static void throwsCheckedException() {
throw new java.io.IOException();
}
}
Это также компилируется:
public class Test {
public static void main(String[] args) throws java.io.IOException {
try {
throwsIOException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
public static void throwsIOException() throws java.io.IOException {
throw new java.io.IOException();
}
}
Более сложный пример - проверенное исключение ловутся внешним блоком catch, вместо того, чтобы быть объявленным броском. Это компилируется:
public class Test {
public static void main(String[] args) {
try {
try {
throwsIOException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
} catch(java.io.IOException e) {
System.out.println("Caught IOException (outer block)");
}
}
public static void throwsIOException() throws java.io.IOException {
throw new java.io.IOException();
}
}
Таким образом, кажется, что есть специальный случай, позволяющий реконструировать исключения, когда компилятор может определить, что пойманное исключение всегда является законным для повторного броска. Это верно? Где это указано в JLS? Существуют ли какие-либо другие неясные угловые случаи, подобные этому?
Ответы
Ответ 1
Это охватывается JLS 11.2.2 (выделено мной):
Оператор throw, чье выраженное выражение конечный или эффективный окончательный параметр исключения предложения catch может вызывать класс исключений E iff:
-
E - это класс исключений, который может выставить блок try оператора try, который объявляет C; и
-
E - это присвоение, совместимое с любым из классов catch catch exception; и
(...)
Другими словами, E
, тип, на который ссылается в doc, - это тип, который может быть выброшен, а не тип параметра catch catch, который его ловит (класс catchable exception). Это просто должно быть назначение, совместимое с параметром catch, но тип параметра не используется в анализе.
Вот почему вы можете сказать окончательный или эффективный окончательный параметр исключения - если t
в вашем примере был переназначен, анализ выйдет из окна.
Ответ 2
Поскольку компилятор достаточно умен, чтобы знать, что исключенное исключение не может быть выбрано из блока try, и пойманное Throwable, таким образом, не является проверенным исключением, которое должно быть объявлено.
Обратите внимание, что это правда с Java 7, если я не ошибаюсь.
Ответ 3
Когда вы ловите Throwable
или Exception
, и эта переменная является фактически окончательной, вы можете перебросить одну и ту же переменную, и компилятор будет знать, какие отмеченные исключения вы могли бы добавить в блок try {} catch
.