2014-05-08 3 views
81

Angenommen, ich eine Klasse und eine MethodeJava 8: Wie arbeite ich mit Exception-Wowing-Methoden in Streams?

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

Nun möchte Ich mag foo für jede Instanz von A durch einen Strom geliefert nennen wie:

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

Frage: Wie richtig ich mit der Ausnahme? Der Code kompiliert nicht auf meinem Computer, da ich nicht die möglichen Ausnahmen behandle, die von foo() ausgelöst werden können. Die throws Exception von bar scheint hier nutzlos zu sein. Warum das?

+2

mögliches Duplikat [Java 8: Lambda-Streams, Filter nach Methode mit Ausnahme] (http://stackoverflow.com/questions/19757300/java-8-lambda-streams-filter-by-method- with-exception) – durron597

+0

Related: http://stackoverflow.com/q/30117134/435605 –

Antwort

78

Sie müssen Ihren Methodenaufruf in einen anderen umbrechen, in dem Sie keine aktivierten Ausnahmen übergeben. Sie können immer noch etwas werfen, das eine Unterklasse von RuntimeException ist.

Eine normale Verpackung Idiom ist so etwas wie:

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

(Supertyp Ausnahme Exception ist nur als Beispiel verwendet wird, nie, es selbst zu fangen versuchen)

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

+1

Wenn Sie möchten, dass es eine Wrapper-Methode ist, würde ich es als statisch deklarieren. Es verwendet nichts von "diesem". – aalku

+114

Es ist traurig, dass wir dies tun müssen, anstatt unsere nativen Ausnahmen zu verwenden ... oh Java, es gibt und dann wegnimmt – Erich

+0

@ kdubb Werfen Sie einen Blick auf Marcgs Antwort: http://StackOverflow.com/a/27661475/363573 . Seine 'UtilException' -Helfer-Klasse bietet einige Arbeit rund um ... – Stephan

6

Sie können Ausnahmen auf diese Weise umbrechen 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); 
    } 
} 
14

Wenn alles, was Sie wollen, ist foo aufzurufen, und Sie es vorziehen, um die Ausnahme zu propagieren, wie (ohne Verpackung) ist, können Sie auch nur for Schleife Java statt (nach dem Stream in einem Iterable Drehen mit einigen trickery):

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

Dies ist zumindest das, was ich in meinen JUnit-Tests zu tun, wo ich will nicht von schlingen meine geprüfte Ausnahmen durch die Mühe gehen (und in der Tat meine Tests bevorzugt die ungeöffneten zu werfen Original)

+0

Oops, danke Tagir Valeev, ja du bist Recht. Ich habe die Antwort aktualisiert, um die [Trickserei] (http://stackoverflow.com/a/20130475/165292) einzuschließen, um einen Stream in einen Iterator umzuwandeln. – avandeursen

12

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

Die eleganteste Art meiner Meinung nach können Sie finden, wurde von Misha hier Aggregate runtime exceptions in Java 8 streams durch Ausführen der Aktionen in "Futures" gegeben. So können Sie alle arbeitenden Teile ausführen und nicht funktionierende Ausnahmen als eine einzige sammeln. Andernfalls könnten Sie sie alle in einer Liste sammeln und später bearbeiten.

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

Je nachdem, was Sie wirklich erreichen möchten, ist eine einfache Zuordnung zwischen den Eingabewerten und den ausgegebenen Werten möglich. Ausnahmen können auch für Sie funktionieren.

Wenn Sie keine dieser Möglichkeiten mögen, sollten Sie (abhängig von der Originalausnahme) zumindest eine eigene Ausnahme in Erwägung ziehen.

+2

Dies sollte als richtige Antwort markiert werden. +. Aber ich kann nicht sagen, dass es ein guter Beitrag ist. Sie sollten es sehr ausführlich ausarbeiten. Wie könnte die eigene Ausnahme helfen? Was sind die aufgetretenen Ausnahmen? Warum hast du die verschiedenen Varianten nicht hierher gebracht? Den gesamten Beispielcode in Referenzen zu haben, wird hier auf SO als sogar verbotener Poststil betrachtet. Ihr Text sieht eher wie ein paar Kommentare aus. – Gangnus

6

Ich schlage vor, Google Guava Throwables Klasse

propagieren (Throwable throwable)

Propagiert throwable zu verwenden, wie sie ist, wenn es sich um eine Instanz von Runtime oder Fehler ist, oder sonst als letztes Mittel, wickelt es in einer RuntimeException und dann propagiert. **

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

Diese Methode wurde (leider nicht mehr unterstützt) (https://github.com/google/guava/wiki/Why-we-deprecated-Throwables.propagate). –

4

Sie können einen der folgenden Schritte tun:

  • propagieren Ausnahme geprüft,
  • es wickeln und ungeprüfte Ausnahme propagieren, oder
  • fangen die Ausnahme und die Ausbreitung stoppen.

Several libraries lassen Sie das leicht tun. Beispiel unten wird 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)));