Почему у меня нет catch для проверенного исключения для вызова, который генерирует общий?
Я работаю над небольшим помощником, который должен вызывать произвольный код (передается как лямбда). Помощник должен поймать определенные исключения и бросить их в какую-нибудь обертку. Мои "собственные" исключения не должны быть обернуты, а просто переброшены.
Я придумал этот код:
@FunctionalInterface
interface Processable<T, X extends Throwable> {
public T apply() throws X;
}
class MyCheckedException extends Exception { ... }
class MyCheckedExceptionWrapper extends MyCheckedException { ... }
public class MyExceptionLogger<T, X extends Throwable> {
public T process(Processable<T, X> processable) throws MyCheckedException {
try {
return processable.apply();
} catch (MyCheckedException thrown) { // this line isn't accepted
throw thrown;
} catch (Exception | LinkageError thrown) {
throw new MyCheckedExceptionWrapper(thrown);
} catch (Throwable thrown) {
... just log
return null;
}
}
}
Вышеприведенная информация дает ошибку компиляции:
Недостижимый блок catch для MyCheckedException. Это исключение никогда не выбрасывается из тела оператора try MyExceptionLogger...
Иными словами: хотя apply()
определено для того, чтобы бросать некоторые X extends Throwable
, я не могу поймать конкретное исключенное исключение при вызове этого метода.
Я знаю, что могу перейти к рабочему коду, поймав Throwable, чтобы использовать instanceof
check - но я хотел бы понять , почему невозможно попробуйте/поймать, как описано выше.
Ответы
Ответ 1
Я думаю, что есть - на основе JSL - нет веской причины, по которой вы не должны улавливать свой выборочный проверенный исключение в своем примере.
Если вы прочтете цитату из JLS
Это ошибка времени компиляции, если предложение catch может уловить проверенный класс исключений E1
, и это не тот случай, когда блок try, соответствующий предложению catch, может выдать проверенный класс исключений, который является подклассом или суперклассом E1
, если E1
не является Exception
или суперклассом Exception
.
Приложению catch должно быть разрешено проверять any Exception
, если метод в соответствующем try-блоке объявляет Throwable
. В вашем примере блок try может выставить проверенный класс исключений, который является подклассом или суперкласс MyCheckedException
, а именно Throwable
и MyCheckedException
, очевидно, не является Exception
или суперклассом Exception
Это можно легко проверить, удалив обобщения из приведенного выше примера и увидев, что он скомпилирован без проблем:
@FunctionalInterface
interface Processable<T> {
public T apply() throws Throwable;
}
private <T> T process(Processable<T> aProcessable) {
try {
return aProcessable.apply();
} catch (MyCheckedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
То есть эта проблема как-то должна быть связана с использованием дженериков в сочетании с исключениями. Возможно, это связано с стиранием типа, но со стираемыми типами ваш пример также отлично работает:
@FunctionalInterface
interface Processable {
public Object apply() throws Throwable;
}
private Object process(Processable aProcessable) {
try {
return aProcessable.apply();
} catch (MyCheckedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
Ответ 2
Это поведение указано в разделе 11.2.3 JLS:
Это ошибка времени компиляции, если предложение catch может уловить проверенный класс исключений E1
, и это не тот случай, когда блок try, соответствующий предложению catch, может выдать проверенный класс исключений, который является подклассом или суперклассом E1
, если E1
не является Exception
или суперклассом Exception
.
MyCheckedException
соответствует описанию класса E1
выше, потому что он не объявлен в общем объявлении processable.apply()
, и это не Exception
или один из его суперклассов. Компилятор знает только, что метод может вызывать Throwable
, поэтому MyCheckedException
не объявляется.