Certaines personnes essaient de vous convaincre que vous devez respecter les règles. Écoutez, mais si vous obéissez, vous devez décider vous-même en fonction de votre situation. En réalité, "vous DEVRIEZ respecter les règles" (et non "vous DEVEZ respecter les règles"). Sachez simplement que si vous ne respectez pas les règles, il pourrait y avoir des conséquences.
Cette situation ne s'applique pas seulement à la situation de Runnable
Avec Java 8, il est également très fréquent que des interfaces fonctionnelles soient introduites dans le contexte des flux et à d'autres endroits sans possibilité de traiter les exceptions vérifiées. Par exemple, Consumer
, Supplier
, Function
, BiFunction
et ainsi de suite, ont tous été déclarés sans possibilité de traiter les exceptions vérifiées.
Quelles sont donc les situations et les options ? Dans le texte ci-dessous, Runnable
est représentatif de toute interface fonctionnelle qui ne déclare pas d'exceptions, ou qui déclare des exceptions trop limitées pour le cas d'utilisation en question.
- Vous avez déclaré
Runnable
quelque part vous-même, et vous pourriez remplacer Runnable
avec quelque chose d'autre.
- Envisagez de remplacer
Runnable
con Callable<Void>
. En gros, c'est la même chose, mais il est autorisé à lancer des exceptions et il doit return null
à la fin, ce qui est un léger désagrément.
- Envisagez de remplacer
Runnable
avec votre propre @FunctionalInterface
qui peut lancer exactement les exceptions que vous voulez.
- Vous avez utilisé une API, et des alternatives sont disponibles. Par exemple, certaines API Java sont surchargées, vous pouvez donc utiliser
Callable<Void>
au lieu de Runnable
.
- Vous avez utilisé une API, et il n'y a pas d'alternatives. Dans ce cas, vous n'êtes pas encore à court d'options.
- Vous pouvez envelopper l'exception dans
RuntimeException
.
- Vous pouvez transformer l'exception en une RuntimeException en utilisant un cast non vérifié.
Vous pouvez essayer ce qui suit. C'est un peu un hack, mais parfois un hack est ce dont nous avons besoin. En effet, le fait qu'une exception doive être vérifiée ou non est défini par son type, mais la pratique devrait en fait être définie par la situation.
@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;
}
Je préfère cela à new RuntimeException(t)
parce qu'il a une trace de pile plus courte.
Vous pouvez maintenant le faire :
executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
Avertissement : la possibilité d'effectuer des castings non vérifiés de cette manière pourrait en fait être supprimée dans les futures versions de Java, lorsque les informations de type générique seront traitées non seulement au moment de la compilation, mais aussi au moment de l'exécution.