Некоторые люди пытаются убедить вас, что вы должны играть по правилам. Слушайте, а подчиняться ли вы, решать вам в зависимости от ситуации. На самом деле «вы ДОЛЖНЫ играть по правилам» (а не «вы ДОЛЖНЫ играть по правилам»). Просто имейте в виду, что если вы не будете играть по правилам, могут быть последствия.
Ситуация применяется не только в случае 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, когда информация о типах обобщенных типов обрабатывается не только во время компиляции, но и во время выполнения.