Java не может поймать общие типы исключений из-за стирание
catch(FooException<Bar> e) --analogy-> o instanceof List<String>
catch
и instanceof
зависят от типа информации во время выполнения; стирание руин это.
Однако, это слишком жестко, чтобы запретить общее объявление типов исключений, таких как FooException<T>
.Java может это позволить; просто требуют, что только сырье типа и подстановочные типа допускаются в catch
catch(FooException e) --analogy-> o instanceof List
catch(FooException<?> e) o instanceof List<?>
Аргумент может быть сделан, что тип исключения в основном интересовались улове пунктами; если мы не сможем поймать общие типы исключений, нет смысла иметь их в первую очередь. ОК.
Теперь, мы не можем сделать это либо
catch(X e) // X is a type variable
тогда почему Java позволяет X
быть выброшен в первую очередь?
... foo() throws X
Как мы увидим позже, эта конструкция, с помощью стирания, может фактически подорвать систему типов.
Ну, эта функция весьма полезна, хотя - по крайней мере, концептуально. Мы пишем общие методы, чтобы мы могли писать код шаблона для неизвестных типов ввода и возврата; та же логика для типов бросков! Например,
<X extends Exception> void logAndThrow(X ex) throws X
...
throw ex;
...
(IOException e){
logAndThrow(e); // throws IOException
Мы должны иметь возможность бросать в общем, чтобы добиться прозрачности исключения при абстрагировании блока кода. Другой пример, скажем, мы устали от написания такого рода шаблонного неоднократно
lock.lock();
try{
CODE
}finally{
lock.unlock();
}
и мы хотим, чтобы метод оболочки для того, принимая в лямбда для КОД
Util.withLock(lock,()->{ CODE });
Проблема в том, КОД может бросать любой тип исключения; исходный шаблон бросает все, что бросает КОД; и мы хотим, чтобы выражение writeLock()
делало то же самое, то есть прозрачность исключения. Решение -
interface Code<X extends Throwable>
{
void run() throws X;
}
<X extends Throwable> void withLock(Lock lock, Code<X> code) throws X
...
code.run(); // throws X
...
withLock(lock,()->{ throws new FooException(); }); // throws FooException
Это относится только к проверенным исключениям. Исключенные исключения могут свободно распространяться.
Одним из серьезных недостатков подхода throws X
является то, что он не работает с 2 или более типами исключений.
Хорошо, это действительно хорошо. Но мы не видим такого использования в новых API JDK. Никакие функциональные типы любого параметра метода не могут вызывать любое проверенное исключение. Если вы делаете Stream.map(x->{ BODY })
, BODY
не может выбрасывать какие-либо проверенные исключения. Они действительно ненавидят проверенные исключения с лямбдами.
Другим примером является CompletableFuture
, где исключение является центральной частью всей концепции; Тем не менее, исключенные исключения не допускаются ни в лямбда-телах.
Если вы мечтатель, вы можете поверить, что в какой-то будущей версии Java будет изобретен элегантный механизм прозрачности исключений для лямбды, ничего подобного уродливому throws X
hack; поэтому теперь нам не нужно загрязнять наши API с помощью <X>
. Да, сон, человек.
Итак, сколько throws X
используется в любом случае?
На протяжении всего источника JDK, единственный публичный API, который делает это Optional.orElseThrow()
(и его кузены OptionalInt/Long/Double
). Вот и все.
(Там же недостаток с orElseGet/orElseThrow
дизайном. - решение разветвленности не может быть сделано в теле лямбда Мы могли бы вместо того, чтобы разработать более общий метод
T orElse(lambda) throws X
optional.orElse(()->{ return x; });
optional.orElse(()->{ throw ex; });
optional.orElse(()->{ if(..)return x; else throw ex; });
Кроме orElseThrow
, единственный другой способ в JDK, что throws X
является непубличной ForkJoinTask.uncheckedThrow()
Он используется для «подлого броска», т.е. код может бросить проверяемое исключение, без компилятора и время выполнения, зная об этом. -
void foo() // no checked exception declared in throws
{
throw sneakyThrow(new IOExcepiton());
}
здесь, IOException
выбрасывается из foo()
во время выполнения; однако компилятор и среда выполнения не смогли его предотвратить. В основном виноват Erasure.
public static RuntimeException sneakyThrow(Throwable t)
{
throw Util.<RuntimeException>sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T
{
throw (T)t;
}
Вы не можете иметь общие типы «Исключения», но вы можете привязывать типы «Исключение» к общим параметрам. –
Кроме того, это не декларация исходного кода, это некоторая бастардизация (границ) в javadoc. –
@SotiriosDelimanolis Я уже это указал. – Mordechai