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

172

Supposons que j'ai une classe et une méthode

class A {
  void foo() throws Exception() {
    ...
  }
}

Maintenant, je voudrais appeler foo pour chaque instance de Alivré par un flux comme:

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 exceptions possibles qui peuvent être levées par foo (). Le throws Exceptionde barsemble être inutile ici. Pourquoi donc?

Bastian
la source
En relation: stackoverflow.com/q/30117134/435605
AlikElzin-kilaka

Réponses:

141

Vous devez encapsuler votre appel de méthode dans un autre, où vous ne lancez pas d' exceptions vérifiées . Vous pouvez toujours lancer tout ce qui est une sous-classe de RuntimeException.

Un idiome 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 Exceptionn'est utilisée qu'à titre d'exemple, n'essayez jamais de l'attraper vous-même)

Ensuite , vous pouvez l' appeler avec: as.forEach(this::safeFoo).

skiwi
la source
1
Si vous voulez que ce soit une méthode wrapper, je la déclarerais statique. Il n'utilise rien de «ceci».
aalku
207
C'est triste que nous devions faire cela au lieu d'utiliser nos exceptions natives .... oh Java, ça donne et puis emporte
Erich
1
@Stephan Cette réponse a été supprimée, mais elle est toujours disponible ici: stackoverflow.com/a/27661562/309308
Michael Mrozek
3
Cela échouerait instantanément à l'examen du code dans mon entreprise: nous ne sommes pas autorisés à lever des exceptions non contrôlées.
Stelios Adamantidis
7
@SteliosAdamantidis cette règle est incroyablement restrictive et contre-productive. savez-vous que toutes les méthodes, dans toutes les api, sont autorisées à lancer toutes les saveurs d'exceptions d'exécution sans même que vous ayez à le savoir (bien sûr que vous l'êtes)? avez-vous interdit javascript pour ne pas avoir implémenté le concept d'exceptions vérifiées? Si j'étais votre développeur principal, j'interdirais plutôt les exceptions cochées.
spi
35

Si tout ce que vous voulez est d'appeler fooet que vous préférez propager l'exception telle quelle (sans wrapping), vous pouvez également simplement utiliser la forboucle de Java à la place (après avoir transformé le Stream en un Iterable avec une astuce ):

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 me donner la peine d'encapsuler mes exceptions vérifiées (et en fait préfère que mes tests jettent les originaux non emballés)

avandeursen
la source
16

Cette question est peut-être un peu vieille, mais parce que je pense que la «bonne» réponse ici n'est qu'une manière qui peut conduire à des problèmes cachés plus tard dans votre code. Même s'il y a une petite controverse , les exceptions vérifiées existent pour une raison.

À mon avis, la manière la plus élégante que vous puissiez trouver a été donnée par Misha ici. Agréger les exceptions d'exécution dans les flux Java 8 en effectuant simplement les actions dans "futures". Ainsi, vous pouvez exécuter toutes les pièces de travail et collecter les exceptions qui ne fonctionnent 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 pièces fonctionnelles et non fonctionnelles.

En fonction de ce que vous voulez vraiment obtenir, un simple mappage entre les valeurs d'entrée et les valeurs de sortie s'est produit Des exceptions peuvent également fonctionner pour vous.

Si vous n'aimez aucune de ces méthodes, envisagez d'utiliser (en fonction de l'exception d'origine) au moins une exception propre.

Mariano
la source
3
Cela doit être marqué comme une réponse correcte. +. Mais je ne peux pas dire que c'est un bon article. Vous devriez l'élaborer considérablement. Comment la propre exception pourrait-elle aider? Quelles sont les exceptions survenues? Pourquoi n'avez-vous pas apporté les différentes variantes ici? Avoir tous les exemples de code dans les références est considéré comme un style de publication même interdit ici sur SO. Votre texte ressemble plutôt à un tas de commentaires.
Gangnus
2
Vous devriez être en mesure de choisir à quel niveau vous voulez les attraper et les streams gâchent cela. L'API Stream devrait vous permettre de porter l'exception jusqu'à l'opération finale (comme collect) et y être gérée avec un gestionnaire ou être levée autrement. stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). Il faut s'attendre à des exceptions et vous permettre de les gérer comme le font les futurs.
aalku
10

Je suggère d'utiliser la classe Google Guava Throwables

propager ( jetable jetable)

Propage le jetable tel quel s'il s'agit d'une instance de RuntimeException ou Error, ou bien en dernier recours, l'enveloppe dans une RuntimeException puis se propage. **

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            throw Throwables.propagate(e);
        }
    });
}

METTRE À JOUR:

Maintenant qu'il est obsolète, utilisez:

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    });
}
yanefedor
la source
4
Cette méthode était obsolète (malheureusement).
Robert Važan
9

Vous pouvez encapsuler et dérouler 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);
    }
}
aalku
la source
9

Vous souhaiterez peut-être effectuer l'une des opérations suivantes:

  • propager l'exception vérifiée,
  • encapsuler et propager l'exception non vérifié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 en utilisant 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)));
Robert Važan
la source
Bon travail à la bibliothèque! J'étais sur le point d'écrire quelque chose de similaire.
Jasper de Vries
2

Manière plus lisible:

class A {
  void foo() throws MyException() {
    ...
  }
}

Il suffit de le cacher dans un RuntimeExceptionpour le faire passerforEach()

  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 qu'à ce stade, je ne tiendrai pas contre quelqu'un s'il dit ignorer les flux et utiliser une boucle for, à moins que:

  • vous ne créez pas votre flux en utilisant Collection.stream(), c'est-à-dire pas une traduction directe en une boucle for.
  • vous essayez d'utiliser parallelstream()
Kashyap
la source