Я довольно озадачен вашим вопросом. В java-исключениях / обработке исключений явно есть что-то, чего вы не понимаете. Итак, начнем с самого начала.
В Java все исключения (в том смысле, что этот термин используется в спецификации языка Java) являются экземплярами некоторого класса, который является подклассом java.lang.Throwable
. Есть два (и только два) прямых подкласса Throwable; то есть java.lang.Exception
и java.lang.Error
. Экземпляры всех этих классов ... включая экземпляры Throwable и Error ... называются исключениями в JLS.
Обработчик исключений перехватывает исключения (в смысле JLS), совместимые по присваиванию с типом исключения, используемым в объявлении catch
. Так, например:
try {
....
} catch (Exception ex) {
...
}
будет перехватывать любое исключение, выброшенное в блоке try
, который является экземпляром java.lang.Exception
или прямого или косвенного подтипа java.lang.Exception
. Но он не поймает экземпляр java.lang.Throwable
, потому что это (очевидно) не один из вышеперечисленных.
С другой стороны:
try {
....
} catch (Throwable ex) {
...
}
будет ловить экземпляр java.lang.Throwable
.
Рассматривая ваш пример в свете этого, становится очевидным, почему метод f
не перехватывает экземпляр Throwable: он не соответствует типу исключения в предложении catch! Напротив, в методе g
экземпляр Exception соответствовал типу исключения в предложении catch и поэтому перехватывается.
Я не понимаю, что вы говорите о необходимости бросить Throwable в g
. Во-первых, тот факт, что метод объявляет, что он выбрасывает Throwable , не означает , что означает, что он действительно должен бросить его. Все, что он говорит, это то, что он может генерировать что-то назначаемое для Throwable ... возможно, в какой-то будущей версии g
метода. Во-вторых, если вы добавите throw e;
к внешнему блоку перехвата, будет создавать что-то, что можно назначить на Throwable.
Наконец, вообще плохая идея - создавать экземпляры Throwable, Exception, Error и RuntimeException. И вам нужно быть очень осторожным, когда и как вы их поймаете. Например:
try {
// throws an IOException if file is missing
InputStream is = new FileInputStream("someFile.txt");
// do other stuff
} catch (Exception ex) {
System.err.println("File not found");
// WRONG!!! We might have caught some completely unrelated exception;
// e.g. a NullPointerException, StackOverflowError,
}
РЕДАКТИРОВАТЬ - в ответ на комментарии ОП:
Но что я выбрасываю с помощью throw e.fillInStackTrace (); должен быть Момент Броска, а не Исключение!
В Javadoc говорится, что возвращаемый объект является объектом исключения, для которого вызывается метод. Цель метода fillInStacktrace()
- заполнить трассировку стека для существующего объекта. Если вы хотите другое исключение, вы должны использовать new
для его создания.
На самом деле, я имею в виду, что обработчик исключений outter не должен перехватывать Throwable, созданный броском e.fillInStackTrace ().
Я объяснил, почему это так - потому что Throwable на самом деле является первоначальным исключением. В моем объяснении есть что-то, что вы не понимаете или просто говорите, что вам не нравится способ определения Java?
РЕДАКТИРОВАТЬ 2
И если обработчик исключений outter мог обработать исключение Throwable, почему мы должны указать, что метод g сгенерирует исключение Throwable
Вы неправильно поняли то, что я говорил ... а именно, что если вы ДЕЙСТВИТЕЛЬНО сгенерировали исключение, то throws Throwable
не будет лишним. ОТО, я наконец-то думаю, что понимаю вашу жалобу.
Я думаю, что суть вашей жалобы в том, что вы получите ошибку компиляции с этим:
public void function() throws Exception {
try {
throw new Exception();
} catch (Exception ex) {
throw ex.fillInStackTrace();
// according to the static type checker, the above throws a Throwable
// which has to be caught, or declared as thrown. But we "know" that the
// exception cannot be anything other than an Exception.
}
}
Я вижу, что это несколько неожиданно. Боюсь, это неизбежно. НЕТ (если не считать серьезных изменений в системе типов Java) НЕТ, чтобы вы могли объявить подпись fillInStacktrace
, которая будет работать во всех случаях. Например, если вы переместили объявление метода в класс Exception, вы просто повторили бы ту же проблему с подтипами Exception. Но если вы попытаетесь выразить подпись с помощью параметра универсального типа, это повлечет за собой создание всех подклассов явных универсальных типов Throwable.
К счастью, лечение действительно простое; приведите результат fillInStacktrace()
следующим образом:
public void function() throws Exception {
try {
throw new Exception();
} catch (Exception ex) {
throw (Exception) (ex.fillInStackTrace());
}
}
И последнее, что для приложения очень необычно явно вызывать fillInStacktrace()
. В свете этого для разработчиков Java просто не стоило бы «ломать голову», пытаясь решить эту проблему. Тем более, что это действительно незначительное неудобство ... самое большее.