2015-03-10 9 views
6

Eccezione Si consideri il seguente codice -Java 8 Fornitore movimentazione

public class TestCompletableFuture { 

    BiConsumer<Integer, Throwable> biConsumer = (x,y) -> { 
     System.out.println(x); 
     System.out.println(y); 
    }; 

    public static void main(String args[]) { 
     TestCompletableFuture testF = new TestCompletableFuture(); 
     testF.start();  
    } 

    public void start() { 
     Supplier<Integer> numberSupplier = new Supplier<Integer>() { 
      @Override 
      public Integer get() { 
       return SupplyNumbers.sendNumbers();      
      } 
     }; 
     CompletableFuture<Integer> testFuture = CompletableFuture.supplyAsync(numberSupplier).whenComplete(biConsumer);   
    }  
} 

class SupplyNumbers { 
    public static Integer sendNumbers(){ 
     return 25; // just for working sake its not correct. 
    } 
} 

La cosa di cui sopra funziona bene. Tuttavia sendNumbers potrebbe anche generare un'eccezione nel mio caso. Come -

class SupplyNumbers { 
    public static Integer sendNumbers() throws Exception { 
     return 25; // just for working sake its not correct. 
    } 
} 

Ora voglio gestire questa eccezione come 'y' nel mio biConsumer. Ciò mi aiuterà a gestire il risultato e l'eccezione (se presente) all'interno di una singola funzione (biConsumer).

Qualche idea? Posso usare CompletableFuture.exceptionally(fn) qui o qualsiasi altra cosa?

risposta

1

Forse si potrebbe utilizzare nuovi oggetti per avvolgere il vostro intero e errore come questo:

public class Result { 

    private Integer integer; 
    private Exception exception; 

    // getter setter 

} 

E poi:

public void start(){ 
    Supplier<Result> numberSupplier = new Supplier<Result>() { 
     @Override 
     public Result get() { 
      Result r = new Result(); 
      try { 
       r.setInteger(SupplyNumbers.sendNumbers()); 
      } catch (Exception e){ 
       r.setException(e); 
      } 
      return r; 

     } 
    }; 
    CompletableFuture<Result> testFuture = CompletableFuture.supplyAsync(numberSupplier).whenComplete(biConsumer); 
} 
6

Sei già recuperando l'eccezione in y. Forse non l'hai visto perché main è terminato prima che il tuo CompletableFuture potesse essere completato?

Il codice qui sotto stampe "null" e "Ciao" come previsto:

public static void main(String args[]) throws InterruptedException { 
    TestCompletableFuture testF = new TestCompletableFuture(); 
    testF.start(); 
    Thread.sleep(1000); //wait for the CompletableFuture to complete 
} 

public static class TestCompletableFuture { 
    BiConsumer<Integer, Throwable> biConsumer = (x, y) -> { 
    System.out.println(x); 
    System.out.println(y); 
    }; 
    public void start() { 
    CompletableFuture.supplyAsync(SupplyNumbers::sendNumbers) 
      .whenComplete(biConsumer); 
    } 
} 

static class SupplyNumbers { 
    public static Integer sendNumbers() { 
    throw new RuntimeException("Hello"); 
    } 
} 
1

io non sono abbastanza sicuro di quello che si sta cercando di ottenere. Se il tuo fornitore emette un'eccezione, quando chiami testFuture .get() otterrai java.util.concurrent.ExecutionException causato da qualsiasi eccezione lanciata dal fornitore, che puoi recuperare chiamando getCause() su ExecutionException.

Oppure, come già menzionato, è possibile utilizzare exceptionally nello CompletableFuture. Questo codice:

public class TestCompletableFuture { 

    private static BiConsumer<Integer, Throwable> biConsumer = (x,y) -> { 
     System.out.println(x); 
     System.out.println(y); 
    }; 

    public static void main(String args[]) throws Exception { 
     Supplier<Integer> numberSupplier =() -> { 
      throw new RuntimeException(); // or return integer 
     }; 

     CompletableFuture<Integer> testFuture = CompletableFuture.supplyAsync(numberSupplier) 
       .whenComplete(biConsumer) 
       .exceptionally(exception -> 7); 

     System.out.println("result = " + testFuture.get()); 
    } 

} 

Stampe questo risultato:

null 
java.util.concurrent.CompletionException: java.lang.RuntimeException 
result = 7 

EDIT:

Se è stata selezionata eccezioni, è sufficiente è possibile aggiungere un try-catch.

Codice originale:

Supplier<Integer> numberSupplier = new Supplier<Integer>() { 
    @Override 
    public Integer get() { 
     return SupplyNumbers.sendNumbers();      
    } 
}; 

Codice modificato:

Supplier<Integer> numberSupplier = new Supplier<Integer>() { 
    @Override 
    public Integer get() { 
     try { 
      return SupplyNumbers.sendNumbers();      
     } catch (Excetpion e) { 
      throw new RuntimeExcetpion(e); 
     } 
    } 
}; 
+0

Funziona solo per le eccezioni non controllati. – Nicholi

+0

@Nicholi A quale punto è stata accennata l'eccezione per la verifica dell'OP? –

+0

@Nicholi Soprattutto per te, ho modificato una risposta e aggiunto un try-catch. Non è stato difficile, vero? –

9

I metodi di fabbrica utilizzando le interfacce funzionali standard non sono utili quando si desidera gestire eccezioni controllate. Quando si inserisce il codice che cattura l'eccezione nell'espressione lambda, si ha il problema che la clausola catch richiede l'istanza CompletableFuture per impostare l'eccezione mentre il metodo factory ha bisogno di Supplier, chicken-and-egg.

È possibile utilizzare un campo di istanza di una classe per consentire la mutazione dopo la creazione, ma alla fine il codice risultante non è pulito e più complicato di una soluzione basata su Executor.Il documentation of CompletableFuture dice:

  • Tutti asincrona metodi senza un argomento esplicito esecutore vengono eseguite utilizzando il ForkJoinPool.commonPool() ...

in modo da sapere il seguente codice mostrerà il comportamento standard di CompletableFuture.supplyAsync(Supplier) mentre gestire le eccezioni controllate in modo diretto:

CompletableFuture<Integer> f=new CompletableFuture<>(); 
ForkJoinPool.commonPool().submit(()-> { 
    try { f.complete(SupplyNumbers.sendNumbers()); } 
    catch(Exception ex) { f.completeExceptionally(ex); } 
}); 

La documentazione dice anche:

... Per semplificare il monitoraggio, il debug e il monitoraggio, tutte le attività asincrone generati sono casi dell'interfaccia marcatore CompletableFuture.AsynchronousCompletionTask.

Se volete aderire a questa convenzione per rendere la soluzione ancora più comportarsi come il metodo originale supplyAsync, modificare il codice per:

CompletableFuture<Integer> f=new CompletableFuture<>(); 
ForkJoinPool.commonPool().submit(
    (Runnable&CompletableFuture.AsynchronousCompletionTask)()-> { 
    try { f.complete(SupplyNumbers.sendNumbers()); } 
    catch(Exception ex) { f.completeExceptionally(ex); } 
}); 
+0

potresti spiegare cosa significa? (Runnable & CompletableFuture.AsynchronousCompletionTask) – Jasonw

+1

@Jasonw: È un cast per un * tipo di intersezione *. In altre parole, l'oggetto deve implementare entrambi i tipi, 'Runnable' * e *' CompletableFuture.AsynchronousCompletionTask'. Poiché i cast di tipi forniscono un tipo di contesto per espressioni lambda, ciò implica che l'istanza lambda generata implementerà entrambe le interfacce. Vedi anche [here] (http://stackoverflow.com/a/22808112/2711488) – Holger

+0

nice lar, impara e casting in lambda oggi. :-) – Jasonw