Некоторые люди пытаются убедить вас, что вы должны играть по правилам. Слушайте, а подчиняться ли вы, решать вам в зависимости от ситуации. На самом деле «вы ДОЛЖНЫ играть по правилам» (а не «вы ДОЛЖНЫ играть по правилам»). Просто имейте в виду, что если вы не будете играть по правилам, могут быть последствия.
Ситуация применяется не только в случае Runnable
, но и с Java 8 очень часто в контексте потоков и других мест, где функциональные интерфейсы были введены без возможности иметь дело с проверенными исключениями. Например, Consumer
, Supplier
, Function
, BiFunction
и так далее все были объявлены без средств для борьбы с проверяемыми исключениями.
Итак, каковы ситуации и варианты? В приведенном ниже тексте Runnable
он представляет любой функциональный интерфейс, который не объявляет исключения или объявляет исключения, слишком ограниченные для рассматриваемого варианта использования.
- Вы
Runnable
сами где-то заявили , и можете заменить Runnable
на что-нибудь другое.
- Рассмотрите возможность замены
Runnable
на Callable<Void>
. По сути то же самое, но с возможностью исключения исключений; и вынужден return null
в конце концов, что немного раздражает.
- Рассмотрите возможность замены
Runnable
на свой собственный, @FunctionalInterface
который может выдавать именно те исключения, которые вам нужны.
- Вы использовали API, и есть альтернативы. Например, некоторые API-интерфейсы Java перегружены, поэтому вы можете использовать
Callable<Void>
вместо Runnable
.
- Вы использовали API, и альтернативы нет. В этом случае у вас все еще есть варианты.
- Вы можете обернуть исключение в
RuntimeException
.
- Вы можете преобразовать исключение в исключение RuntimeException с помощью непроверенного приведения.
Вы можете попробовать следующее. Это немного похоже на хакерство, но иногда это то, что нам нужно. Потому что, следует ли проверять исключение или снимать его, определяется его типом, но на практике это должно определяться ситуацией.
@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
@Override
default void run() {
try {
tryRun();
} catch (final Throwable t) {
throwUnchecked(t);
}
}
private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
throw (E) t;
}
void tryRun() throws Throwable;
}
Я предпочитаю это, new RuntimeException(t)
потому что у него более короткая трассировка стека.
Теперь вы можете:
executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
Отказ от ответственности: возможность выполнять неконтролируемое приведение таким образом может фактически быть удалена в будущих версиях Java, когда информация о типах обобщенных типов обрабатывается не только во время компиляции, но и во время выполнения.