Problema: come posso lanciare l'eccezione e far sì che l'iterazione del flusso sappia che non deve interrompere l'intera iterazione, ma continuare con l'elemento successivo (e infine registrare gli oggetti non riusciti)?Come ripetere un flusso anche se vengono lanciate eccezioni?
risposta
Senza un'eccezione si può lavorare con Optional:
stream.map(obj -> doMap(obj))
.filter(obj -> obj.isPresent())
.collect(Collectors.toList());
private Optional<String> doMap(Object obj) {
if (objectIsInvalid) {
return Optional.empty();
}
}
Suppongo che dipenda da cosa stai facendo, ma sopprimere silenziosamente le eccezioni di solito è una pessima idea. Se qualcuno ha passato dati non validi, o se l'utente ha specificato un input non valido, dovresti dirglielo, non comportarsi come se tutto fosse a posto. – VGR
Non so perché si desidera "lavorare con nulls * e * Optionals". Normalmente dovresti decidere per * o *, usando 'null' o usando' Optional'. Il tuo codice restituisce 'null' in' doMap' e prova a richiamare '.isPresent()' su di esso all'interno di 'filter (...)' ... – Holger
Per favore non farlo. Se qualcosa non è valido, probabilmente si desidera restituire un * vuoto * 'Opzionale' invece di' null'. Così com'è, otterrai NPE quando 'isPresent' è chiamato su' null'. –
Se si desidera un mezzo per ignorare tranquillamente quegli elementi che un'eccezione quando mappata, è possibile definire un metodo di supporto che o restituisce un successo o niente , come stream di 0 o 1 elementi, quindi flatMap il tuo stream con quel metodo.
import static java.util.stream.Collectors.toList;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Fish {
private int weight;
public Fish(int weight) { this.weight = weight; }
public String size() {
if (weight < 10) {
return "small";
} else if (weight < 50) {
return "medium";
} else if (weight < 100) {
return "large";
} else {
throw new RuntimeException("I don't believe you");
}
}
public static <T> Stream<T> successOrNothing(Supplier<T> supplier) {
try {
return Stream.of(supplier.get());
} catch (Exception e) {
return Stream.empty();
}
}
public static void main(String[] args) {
Stream<Fish> fishes = Stream.of(new Fish(1000), new Fish(2));
List<String> sizes = fishes
.flatMap(fish -> successOrNothing(fish::size))
.collect(toList());
System.out.println(sizes);
}
}
Ogni elemento nel flusso viene mappato su 1 elemento (se la chiamata è tornato con successo) o 0 (se non ha). Questo potrebbe essere più semplice o più semplice della "modifica del metodo per restituire null o Optional.empty() anziché lanciare" pattern, specialmente quando si ha a che fare con un metodo il cui contratto è già definito e che non si desidera modificare.
Avvertenza: questa soluzione ignora silenziosamente le eccezioni; è inappropriato in molte situazioni.
Ecco uno strano trucco che è possibile utilizzare per migliorare la gestione delle eccezioni.
Diciamo che la vostra funzione mapper è come questo:
String doMap(Object obj) {
if (isInvalid(obj)) {
throw new IllegalArgumentException("info about obj");
} else {
return obj.toString();
}
}
Questo restituisce un risultato se l'oggetto è valida, ma viene generata un'eccezione se l'oggetto non è valido. Sfortunatamente se lo si configura direttamente in una pipeline, qualsiasi errore interromperà l'esecuzione della pipeline. Quello che vuoi è qualcosa come un tipo "o" che può contenere un valore o un indicatore di errore (che sarebbe un'eccezione in Java).
Risulta che CompletableFuture
può contenere un valore o un'eccezione. Sebbene sia destinato all'elaborazione asincrona, che non si verifica qui, dobbiamo solo contenerlo un po 'per utilizzarlo per i nostri scopi.
Innanzitutto, dato un stream
di oggetti da elaborare, chiamiamo la funzione di mappatura avvolto in una chiamata a supplyAsync
:
CompletableFuture<String>[] cfArray =
stream.map(obj -> CompletableFuture.supplyAsync(() -> doMap(obj), Runnable::run))
.toArray(n -> (CompletableFuture<String>[])new CompletableFuture<?>[n]);
(Purtroppo, la creazione generico array dà un avvertimento incontrollato, che dovrà essere soppresso.)
il costrutto strana
CompletableFuture.supplyAsync(supplier, Runnable::run)
corre il fornitore "asincrono" sulla condizione Esecutore Runnable::run
, che esegue semplicemente l'attività immediatamente in questo thread. In altre parole, esegue il fornitore in modo sincrono.
Il trucco è che l'istanza CompletableFuture
restituita da questa chiamata contiene il valore dal fornitore, se viene restituito normalmente o contiene un'eccezione, se il fornitore ne ha gettato uno. (Sto ignorando l'annullamento qui.) Quindi raccogliamo le istanze CompletableFuture
in una matrice. Perché un array? È impostato per la parte successiva:
CompletableFuture.allOf(cfArray).join();
Questo normalmente attende che l'array di CF si completi.Dal momento che sono stati eseguiti in modo sincrono, dovrebbero già essere tutti completi. Ciò che è importante per questo caso è che join()
genererà uno CompletionException
se qualsiasi dei CF nell'array è stato completato in modo eccezionale. Se il join termina normalmente, possiamo semplicemente raccogliere i valori di ritorno. Se il join genera un'eccezione, possiamo propagarlo o possiamo prenderlo ed elaborare le eccezioni memorizzate nei CF dell'array. Ad esempio,
try {
CompletableFuture.allOf(cfArray).join();
// no errors
return Arrays.stream(cfArray)
.map(CompletableFuture::join)
.collect(toList());
} catch (CompletionException ce) {
long errcount =
Arrays.stream(cfArray)
.filter(CompletableFuture::isCompletedExceptionally)
.count();
System.out.println("errcount = " + errcount);
return Collections.emptyList();
}
Se tutti hanno esito positivo, restituisce un elenco dei valori. Se ci sono delle eccezioni, questo conta il numero di eccezioni e restituisce una lista vuota. Naturalmente, si potrebbe facilmente fare qualcosa di diverso, come registrare i dettagli delle eccezioni, filtrare le eccezioni e restituire un elenco di valori validi, ecc
Farei seguente ...
stream.map(doMapLenient())
.filter(Objects::nonNull)
.collect(Collectors.toList());
private String doMap(Object obj) {
if (objectIsInvalid(obj)) {
throw new ParseException("Object could not be parsed");
}
return "Streams are cool";
}
private Function<Object, String> doMapLenient() {
return obj -> {
try {
return doMap(obj);
} catch (ParseExcepion ex) {
return null;
}
}
qui puoi aggiungere anche un po 'di logging nella parte di cattura
- 1. Come vedi quali eccezioni vengono lanciate con Intellisense in VS2015?
- 2. Come posso verificare se sono già state lanciate delle eccezioni?
- 3. Eccezioni lanciate in jQuery Chiamate AJAX ingerite?
- 4. Making Struts invia 500 Internal Server Error quando vengono lanciate eccezioni
- 5. Come specificare le eccezioni che devono essere lanciate da un implementatore di un'interfaccia?
- 6. I metadati Blob non vengono salvati anche se chiamo CloudBlob.SetMetadata
- 7. C#: sollevate o lanciate un'eccezione?
- 8. ASP.NET MVC: dove vengono generate le eccezioni?
- 9. C'è qualche riferimento autorevole su quali eccezioni possono essere lanciate dalle funzioni built-in di Matlab?
- 10. Perché l'elaborazione tenta di catturare senza eccezioni lanciate senza rallentare il programma?
- 11. Come sapere se un flusso BufferedReader è chiuso
- 12. Le date precedenti vengono analizzate come ora legale, anche se ciò non è vero in Java
- 13. Intellij contrassegna tutti i metodi come non utilizzati anche se vengono utilizzati
- 14. Anche i tag git vengono spinti?
- 15. Qual è la differenza tra un flusso di lavoro e un diagramma di flusso, se presente?
- 16. java.lang.OutOfMemoryError anche se un sacco di
- 17. Diagramma di flusso altro se
- 18. Perché le eccezioni in C++ non vengono controllate dal compilatore?
- 19. XMLStreamReader e un flusso reale
- 20. C'è un modo per forzare JUnit a fallire su QUALSIASI eccezione non controllata, anche se ingerita
- 21. Come ripetere un set di caratteri
- 22. Ottengo "TypeError: le eccezioni devono derivare da BaseException" anche se l'ho definito
- 23. Come ripetere una fase in Jenkins Workflow
- 24. java.io.NotSerializableException anche se implemento "Serializable"
- 25. Verifica se un flusso è un file zip
- 26. C# che definisce in modo esplicito quali eccezioni vengono generate
- 27. Eccezioni Java Eccezioni C++
- 28. Tutte le immagini di sfondo CSS vengono caricate anche se il CSS non viene applicato?
- 29. Perché un percorso funziona anche se contiene un @ prima di "\\"
- 30. Visual Studio che mostra errori anche se i progetti vengono compilati con successo in Razor html
Se non vuoi restituire nulla quando viene lanciata un'eccezione, forse dovresti usare 'flatmap' invece di' map'. – Balduz
Forse puoi semplicemente restituire null invece di una stringa e filtrare su oggetti non nulli? – Davio
@membersound, è 'ParseException' qualcosa si è creato da soli, o' java.text.ParseException'? – aioobe