Supposons que j'ai une classe et une méthode
class A {
void foo() throws Exception() {
...
}
}
Maintenant, j'aimerais appeler foo pour chaque instance de A
délivrée par un flux tel que:
void bar() throws Exception {
Stream<A> as = ...
as.forEach(a -> a.foo());
}
Question: Comment gérer correctement l'exception? Le code ne se compile pas sur ma machine car je ne gère pas les éventuelles exceptions pouvant être levées par foo (). Le throws Exception
de bar
semble être inutile ici. Pourquoi donc?
Vous devez envelopper votre appel de méthode dans un autre, où vous ne lancez pas exceptions cochées. Vous pouvez toujours lancer tout ce qui est une sous-classe de RuntimeException
.
Un langage d'emballage normal est quelque chose comme:
private void safeFoo(final A a) {
try {
a.foo();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
(L'exception de supertype Exception
est niquement utilisée à titre d'exemple, n'essayez jamais de l'attraper vous-même)
Ensuite, vous pouvez l'appeler avec: as.forEach(this::safeFoo)
.
Si vous souhaitez simplement appeler foo
et si vous préférez propager l’exception telle quelle (sans le wrapping), vous pouvez également utiliser la boucle for
de Java à la place (après avoir transformé le Stream en Iterable avec ruse ):
for (A a : (Iterable<A>) as::iterator) {
a.foo();
}
C’est du moins ce que je fais dans mes tests JUnit, où je ne veux pas avoir la peine d’emballer mes exceptions vérifiées (et préfère en fait que mes tests jettent les originaux non emballés)
Cette question est peut-être un peu ancienne, mais parce que je pense que la "bonne" réponse ici n’est qu’un des moyens de générer des problèmes cachés plus loin dans votre code. Même s'il y a un peu de controverse , les exceptions vérifiées existent pour une raison.
La façon la plus élégante, à mon avis, que vous puissiez trouver a été donnée par Misha ici agrégations des exceptions d’exécution dans les flux Java 8 simplement en effectuant les actions dans les "futures". Ainsi, vous pouvez exécuter toutes les parties actives et collecter les exceptions ne fonctionnant pas en une seule. Sinon, vous pouvez tous les rassembler dans une liste et les traiter plus tard.
Une approche similaire vient de Benji Weber . Il suggère de créer un type propre pour collecter les parties actives et non actives.
En fonction de ce que vous voulez vraiment réaliser, une correspondance entre les valeurs d'entrée et les valeurs de sortie apparues. Des exceptions peuvent également fonctionner pour vous.
Si vous n'aimez aucune de ces méthodes, envisagez d'utiliser (selon l'exception originale) au moins une propre exception.
Je suggère d'utiliser la classe Google Guava Throwables
propager (Throwable jetable)
Propage le jetable tel quel s'il s'agit d'une instance de RuntimeException ou Error, ou encore, en dernier recours, l'enveloppe dans un RuntimeException, puis se propage. **
void bar() {
Stream<A> as = ...
as.forEach(a -> {
try {
a.foo()
} catch(Exception e) {
throw Throwables.propagate(e);
}
});
}
MISE À JOUR:
Maintenant que c'est déconseillé, utilisez:
void bar() {
Stream<A> as = ...
as.forEach(a -> {
try {
a.foo()
} catch(Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
});
}
Vous pouvez encapsuler et décompresser les exceptions de cette façon.
class A {
void foo() throws Exception {
throw new Exception();
}
};
interface Task {
void run() throws Exception;
}
static class TaskException extends RuntimeException {
private static final long serialVersionUID = 1L;
public TaskException(Exception e) {
super(e);
}
}
void bar() throws Exception {
Stream<A> as = Stream.generate(()->new A());
try {
as.forEach(a -> wrapException(() -> a.foo())); // or a::foo instead of () -> a.foo()
} catch (TaskException e) {
throw (Exception)e.getCause();
}
}
static void wrapException(Task task) {
try {
task.run();
} catch (Exception e) {
throw new TaskException(e);
}
}
Vous voudrez peut-être faire l'une des choses suivantes:
Plusieurs bibliothèques vous permettent de le faire facilement. L'exemple ci-dessous est écrit à l'aide de ma bibliothèque NoException .
// Propagate checked exception
as.forEach(Exceptions.sneak().consumer(A::foo));
// Wrap and propagate unchecked exception
as.forEach(Exceptions.wrap().consumer(A::foo));
as.forEach(Exceptions.wrap(MyUncheckedException::new).consumer(A::foo));
// Catch the exception and stop propagation (using logging handler for example)
as.forEach(Exceptions.log().consumer(Exceptions.sneak().consumer(A::foo)));
Manière plus lisible:
class A {
void foo() throws MyException() {
...
}
}
Cachez-le simplement dans un RuntimeException
pour le faire passer forEach()
void bar() throws MyException {
Stream<A> as = ...
try {
as.forEach(a -> {
try {
a.foo();
} catch(MyException e) {
throw new RuntimeException(e);
}
});
} catch(RuntimeException e) {
throw (MyException) e.getCause();
}
}
Bien que, à ce stade, je ne m'en tiens pas à quelqu'un s'il dit ignorer les flux et opter pour une boucle for, sauf si:
Collection.stream()
, c'est-à-dire d'une traduction directe vers une boucle for.parallelstream()