5

Ich habe den folgenden Code:Ausnahme von CompletableFuture Werfen

// How to throw the ServerException? 
public void myFunc() throws ServerException{ 
    // Some code 
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> { 
     try { 
      return someObj.someFunc(); 
     } catch(ServerException ex) { 
      // throw ex; gives an error here. 
     } 
    })); 
    // Some code 
} 

someFunc() ein ServerException wirft. Ich möchte das hier nicht behandeln, sondern die Ausnahme von someFunc() zum Aufrufer von myFunc() werfen.

Antwort

11

Ihr Code schlägt vor, dass Sie das Ergebnis des verwenden später asynchroner Betrieb in der gleichen Methode, so dass Sie auf jeden Fall mit CompletionException zu tun haben werden, so eine Art und Weise, damit umzugehen, sind

public void myFunc() throws ServerException { 
    // Some code 
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> { 
     try { return someObj.someFunc(); } 
     catch(ServerException ex) { throw new CompletionException(ex); } 
    }); 
    // Some code running in parallel to someFunc() 

    A resultOfA; 
    try { 
     resultOfA = a.join(); 
    } 
    catch(CompletionException ex) { 
     try { 
      throw ex.getCause(); 
     } 
     catch(Error|RuntimeException|ServerException possible) { 
      throw possible; 
     } 
     catch(Throwable impossible) { 
      throw new AssertionError(impossible); 
     } 
    } 
    // some code using resultOfA 
} 

Alle Ausnahmen innerhalb der asynchronen Verarbeitung der Supplier geworfen wird in eine bekommen gewickelten CompletionException beim Aufruf join, außer der ServerException haben wir bereits in eine CompletionException gewickelt.

Wenn wir wieder wirft die Ursache der CompletionException, können wir nicht markiert Ausnahmen, das heißt Subklassen von Error oder RuntimeException oder unseren eigenen geprüfte Ausnahme ServerException Gesicht. Der obige Code behandelt alle von ihnen mit einem Mehrfach-Fang, der sie erneut werfen wird. Da der deklarierte Rückgabetyp getCause()Throwable ist, verlangt der Compiler, dass wir diesen Typ behandeln, obwohl wir bereits alle möglichen Typen behandelt haben. Die unkomplizierte Lösung ist es, diese eigentlich unmöglich werfen in eine AssertionError gewickelt werfen.

Alternativ könnten wir ein alternatives Ergebnis Zukunft für unsere benutzerdefinierte Ausnahme verwenden:

public void myFunc() throws ServerException { 
    // Some code 
    CompletableFuture<ServerException> exception = new CompletableFuture<>(); 
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> { 
     try { return someObj.someFunc(); } 
     catch(ServerException ex) { 
      exception.complete(ex); 
      throw new CompletionException(ex); 
     } 
    }); 
    // Some code running in parallel to someFunc() 

    A resultOfA; 
    try { 
     resultOfA = a.join(); 
    } 
    catch(CompletionException ex) { 
     if(exception.isDone()) throw exception.join(); 
     throw ex; 
    } 

    // some code using resultOfA 
} 

Diese Lösung wird alle „unerwartet“ Throwables in ihre eingewickelt Form wieder werfen, sondern nur die benutzerdefinierte ServerException in seiner ursprünglichen werfen Formular über die exception Zukunft übergeben. Beachten Sie, dass wir sicherstellen müssen, dass a abgeschlossen wurde (wie zum Beispiel join() zuerst), bevor wir die exception Zukunft abfragen, um Race-Bedingungen zu vermeiden.

+0

das ist so schön ... – Eugene

+0

sehr detaillierte Antwort. –

2

denke ich, dass Sie, dass in ein RuntimeException wickeln sollen und werfen das:

throw new RuntimeException(ex); 

Oder viele ein kleines Programm helfen würden:

static class Wrapper extends RuntimeException { 

    private Wrapper(Throwable throwable) { 
     super(throwable); 
    } 

    public static Wrapper wrap(Throwable throwable) { 
     return new Wrapper(throwable); 
    } 

    public Throwable unwrap() { 
     return getCause(); 
    } 
} 


public static void go() { 
    CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> { 
     try { 
      throw new Exception("Just because"); 
     } catch (Exception ex) { 
      throw Wrapper.wrap(ex); 
     } 
    }); 

    a.join(); 
} 

Und dann kann man unwrap dass ..

try { 
     go(); 
} catch (Wrapper w) { 
     throw w.unwrap(); 
} 
+0

ich brauche nur ein 'ServerException' zu werfen. – ayushgp

+1

@ayushgp Ich sehe das nicht mit Standard-Streams passiert, da sie keine überprüften Ausnahmen zulassen ... könnten Sie mit dem Einpacken und dem Auspacken in Ordnung sein? – Eugene

+2

Es sieht so aus, als würde Ihre 'go()' Methode niemals etwas werfen. Ich denke, es fehlt ein 'Join()' Anruf. Außerdem bietet 'Wrapper' nicht viel mehr als das, was bereits mit' Throwable.getCause() 'verfügbar ist. Ich würde eine Ausnahme nicht in eine andere umbrechen, ohne die Ursache festzulegen, da sie die Konvention bricht und keine richtigen Stacktraces erzeugt. –

0

Auch wenn die andere Antwort sehr nett ist. aber ich gebe Ihnen eine andere Möglichkeit, eine checked-exception in CompletableFuture zu werfen.

IF Sie keine CompletableFuture in einem anderen Thread aufrufen wollen, können Sie eine anonyme Klasse verwenden sie zu handhaben, der Code wie folgt:

CompletableFuture<A> a = new CompletableFuture<A>() {{ 
    try { 
     complete(someObj.someFunc()); 
    } catch (ServerException ex) { 
     completeExceptionally(ex); 
    } 
}}; 

IF Sie aufrufen möchten CompletableFuture in einem anderen Thread, können Sie auch eine anonyme Klasse verwenden sie zu handhaben, aber das Verfahren durch runAsync laufen:

CompletableFuture<A> a = new CompletableFuture<A>() {{ 
    CompletableFuture.runAsync(() -> { 
     try { 
      complete(someObj.someFunc()); 
     } catch (ServerException ex) { 
      completeExceptionally(ex); 
     } 
    }); 
}}; 
+4

Dies ist in einer anonymen Unterklasse nicht erforderlich. Die Unterklasse verschwendet nur Ressourcen. Siehe auch [hier] (https://stackoverflow.com/a/43767613/2711488) und [hier] (https://stackoverflow.com/a/28961083/2711488) ... – Holger

+0

@Holger danke, mein Herr. Ich schreibe es nur in Gedanken auf. und ich werde es später sehen. –

+0

@Holger Herr, ich fand Ihre zwei Antworten sind unterschiedlich. und ich bevorzuge deine erste, die du in dieser Frage benutzt hast. weil es einfach zu bedienen und sehr klar ist. –