web-dev-qa-db-fra.com

Java 8: Comment utiliser les méthodes de lancement d'exceptions dans les flux?

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?

147
Bastian

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).

130
skiwi

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)

26
avandeursen

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.

13
Mariano

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);
        }
    });
}
10
fbokovikov

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);
    }
}
7
aalku

Vous voudrez peut-être faire l'une des choses suivantes:

  • propager l'exception vérifiée,
  • l'envelopper et propager l'exception non cochée, ou
  • attraper l'exception et arrêter la propagation.

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)));
7
Robert Važan

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:

  • vous ne créez pas votre flux à l'aide de Collection.stream(), c'est-à-dire d'une traduction directe vers une boucle for.
  • vous essayez d'utiliser parallelstream()
1
Kashyap