Java 8: Wie arbeite ich mit Methoden zum Auslösen von Ausnahmen in Streams?

172

Angenommen, ich habe eine Klasse und eine Methode

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

Jetzt möchte ich foo für jede Avon einem Stream gelieferte Instanz aufrufen wie:

void bar() throws Exception {
  Stream<A> as = ...
  as.forEach(a -> a.foo());
}

Frage: Wie gehe ich mit der Ausnahme richtig um? Der Code wird auf meinem Computer nicht kompiliert, da ich die möglichen Ausnahmen, die von foo () ausgelöst werden können, nicht behandle. Das throws Exceptionvon barscheint hier nutzlos zu sein. Warum ist das so?

Bastian
quelle
3
Mögliches Duplikat von Java 8: Lambda-Streams, Filter nach Methode mit Ausnahme
durron597
Siehe auch
AlikElzin-kilaka

Antworten:

141

Sie müssen Ihren Methodenaufruf in einen anderen umschließen, in dem Sie keine aktivierten Ausnahmen auslösen . Sie können immer noch alles werfen, was eine Unterklasse von ist RuntimeException.

Eine normale Verpackungssprache ist so etwas wie:

private void safeFoo(final A a) {
    try {
        a.foo();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

(Die Supertyp-Ausnahme Exceptionwird nur als Beispiel verwendet. Versuchen Sie niemals, sie selbst abzufangen.)

Dann können Sie es aufrufen mit : as.forEach(this::safeFoo).

Skiwi
quelle
1
Wenn Sie möchten, dass es sich um eine Wrapper-Methode handelt, würde ich sie als statisch deklarieren. Es wird nichts von "diesem" verwendet.
Aalku
206
Es ist traurig, dass wir dies tun müssen, anstatt unsere nativen Ausnahmen zu verwenden ... oh Java, es gibt und nimmt dann weg
Erich
1
@Stephan Diese Antwort wurde gelöscht, ist aber hier noch verfügbar: stackoverflow.com/a/27661562/309308
Michael Mrozek
3
Das würde die Codeüberprüfung in meinem Unternehmen sofort scheitern lassen: Wir dürfen keine ungeprüften Ausnahmen auslösen.
Stelios Adamantidis
7
@SteliosAdamantidis diese Regel ist unglaublich restriktiv und kontraproduktiv. Ist Ihnen bewusst, dass alle Methoden in allen APIs berechtigt sind, alle Arten von Laufzeitausnahmen auszulösen, ohne dass Sie es überhaupt wissen müssen (natürlich sind Sie es)? Haben Sie Javascript verboten, weil das Konzept der geprüften Ausnahmen nicht implementiert wurde? Wenn ich Ihr Hauptentwickler wäre, würde ich stattdessen geprüfte Ausnahmen verbieten.
Spi
35

Wenn Sie nur aufrufen möchten foound die Ausnahme lieber forunverändert weitergeben möchten (ohne Umbruch), können Sie stattdessen auch einfach die Java- Schleife verwenden (nachdem Sie den Stream mit einigen Tricks in eine Iterable verwandelt haben ):

for (A a : (Iterable<A>) as::iterator) {
   a.foo();
}

Dies ist zumindest das, was ich in meinen JUnit-Tests mache, in denen ich nicht die Mühe machen möchte, meine geprüften Ausnahmen zu verpacken (und meine Tests tatsächlich den nicht verpackten Originalausnahmen vorzuziehen).

avandeursen
quelle
16

Diese Frage mag etwas alt sein, aber weil ich denke, dass die "richtige" Antwort hier nur eine Möglichkeit ist, die später in Ihrem Code zu versteckten Problemen führen kann. Auch wenn es ein wenig kontrovers ist , gibt es aus einem bestimmten Grund geprüfte Ausnahmen.

Der meiner Meinung nach eleganteste Weg, den Sie finden können, wurde von Misha hier angegeben. Aggregieren Sie Laufzeitausnahmen in Java 8-Streams, indem Sie nur die Aktionen in "Futures" ausführen. So können Sie alle Arbeitsteile ausführen und nicht funktionierende Ausnahmen als eine einzige sammeln. Andernfalls könnten Sie sie alle in einer Liste sammeln und später verarbeiten.

Ein ähnlicher Ansatz stammt von Benji Weber . Er schlägt vor, einen eigenen Typ zu erstellen, um funktionierende und nicht funktionierende Teile zu sammeln.

Je nachdem, was Sie wirklich erreichen möchten, erfolgt eine einfache Zuordnung zwischen den Eingabewerten und den aufgetretenen Ausgabewerten. Ausnahmen können auch für Sie funktionieren.

Wenn Ihnen eine dieser Möglichkeiten nicht gefällt, sollten Sie (abhängig von der ursprünglichen Ausnahme) mindestens eine eigene Ausnahme verwenden.

Mariano
quelle
3
Dies sollte als richtige Antwort markiert werden. +. Aber ich kann nicht sagen, dass es ein guter Beitrag ist. Sie sollten es sehr ausarbeiten. Wie könnte die eigene Ausnahme helfen? Was sind die aufgetretenen Ausnahmen? Warum haben Sie die verschiedenen Varianten nicht hierher gebracht? Alle Beispielcodes in Referenzen zu haben, wird hier auf SO als sogar verbotener Post-Stil angesehen. Ihr Text sieht eher wie eine Reihe von Kommentaren aus.
Gangnus
2
Sie sollten in der Lage sein zu wählen, auf welcher Ebene Sie sie fangen möchten, und Streams damit herumspielen. Mit der Stream-API sollten Sie die Ausnahme bis zur letzten Operation (wie z. B. Sammeln) ausführen und dort mit einem Handler behandelt oder auf andere Weise ausgelöst werden. stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). Es sollte Ausnahmen erwarten und es Ihnen ermöglichen, sie wie Futures zu behandeln.
Aalku
10

Ich schlage vor, die Google Guava Throwables-Klasse zu verwenden

vermehren ( Wurfbar )

Propagiert den Wurf so wie er ist, wenn es sich um eine Instanz von RuntimeException oder Error handelt, oder als letzten Ausweg, umschließt ihn in eine RuntimeException und propagiert ihn dann. **

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

AKTUALISIEREN:

Jetzt, da es veraltet ist, verwenden Sie:

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    });
}
Yanefedor
quelle
4
Diese Methode wurde als veraltet (leider).
Robert Važan
9

Auf diese Weise können Sie Ausnahmen ein- und auspacken.

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
quelle
9

Möglicherweise möchten Sie einen der folgenden Schritte ausführen:

  • Propagierte Ausnahme verbreiten,
  • wickeln Sie es ein und verbreiten Sie ungeprüfte Ausnahmen, oder
  • Fang die Ausnahme und stoppe die Weitergabe.

In mehreren Bibliotheken können Sie dies problemlos tun. Das folgende Beispiel wurde mit meiner NoException- Bibliothek geschrieben.

// 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
quelle
Gute Arbeit in der Bibliothek! Ich wollte gerade etwas Ähnliches schreiben.
Jasper de Vries
2

Besser lesbarer Weg:

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

Verstecke es einfach in einem RuntimeException, um es vorbei zu bringenforEach()

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

Obwohl ich mich an dieser Stelle nicht gegen jemanden behaupten werde, wenn er sagt, überspringe die Streams und gehe mit einer for-Schleife, es sei denn:

  • Sie erstellen Ihren Stream nicht mit Collection.stream(), dh nicht mit einer einfachen Übersetzung in eine for-Schleife.
  • Sie versuchen zu verwenden parallelstream()
Kashyap
quelle