Sto aggiungendo questa seconda risposta basata su una modifica proposta dall'utente srborlongan all'altra mia risposta . Penso che la tecnica proposta fosse interessante, ma non era davvero adatta come modifica alla mia risposta. Altri hanno concordato e la modifica proposta è stata votata verso il basso. (Non ero uno degli elettori.) La tecnica ha comunque dei meriti. Sarebbe stato meglio se srborlongan avesse pubblicato la propria risposta. Questo non è ancora successo e non volevo che la tecnica andasse persa nelle nebbie della cronologia delle modifiche rifiutata da StackOverflow, quindi ho deciso di presentarla come risposta separata.
Fondamentalmente la tecnica consiste nell'utilizzare alcuni dei Optional
metodi in modo intelligente per evitare di dover usare un operatore ternario ( ? :
) o un'istruzione if / else.
Il mio esempio inline verrebbe riscritto in questo modo:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
Un mio esempio che utilizza un metodo helper verrebbe riscritto in questo modo:
/**
* Turns an Optional<T> into a Stream<T> of length zero or one depending upon
* whether a value is present.
*/
static <T> Stream<T> streamopt(Optional<T> opt) {
return opt.map(Stream::of)
.orElseGet(Stream::empty);
}
Optional<Other> result =
things.stream()
.flatMap(t -> streamopt(resolve(t)))
.findFirst();
COMMENTO
Confrontiamo direttamente le versioni originali vs modificate:
// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
L'originale è un approccio semplice se simile all'operaio: otteniamo un Optional<Other>
; se ha un valore, restituiamo un flusso contenente quel valore e se non ha alcun valore, restituiamo un flusso vuoto. Abbastanza semplice e facile da spiegare.
La modifica è intelligente e ha il vantaggio di evitare i condizionali. (So che ad alcune persone non piace l'operatore ternario. Se usato in modo improprio può davvero rendere il codice difficile da capire.) Tuttavia, a volte le cose possono essere troppo intelligenti. Il codice modificato inizia anche con un Optional<Other>
. Quindi chiama Optional.map
che è definito come segue:
Se è presente un valore, applicare ad esso la funzione di mapping fornita e, se il risultato è diverso da null, restituire un Opzionale che descrive il risultato. Altrimenti restituire un Opzionale vuoto.
La map(Stream::of)
chiamata restituisce un Optional<Stream<Other>>
. Se un valore era presente nell'input facoltativo, l'Opzionale restituito contiene un flusso che contiene il singolo altro risultato. Ma se il valore non era presente, il risultato è un Opzionale vuoto.
Successivamente, la chiamata a orElseGet(Stream::empty)
restituisce un valore di tipo Stream<Other>
. Se il suo valore di input è presente, ottiene il valore, che è l'elemento singolo Stream<Other>
. Altrimenti (se il valore di input è assente) restituisce un valore vuoto Stream<Other>
. Quindi il risultato è corretto, lo stesso del codice condizionale originale.
Nei commenti sulla mia risposta, riguardo alla modifica rifiutata, avevo descritto questa tecnica come "più concisa ma anche più oscura". Sono qui. Mi ci è voluto un po 'di tempo per capire cosa stesse facendo e mi ci è voluto anche un po' per scrivere la descrizione sopra di quello che stava facendo. La sottigliezza chiave è la trasformazione da Optional<Other>
a Optional<Stream<Other>>
. Una volta che hai capito, ha senso, ma non era ovvio per me.
Riconoscerò, tuttavia, che le cose inizialmente oscure possono diventare idiomatiche nel tempo. Potrebbe essere che questa tecnica finisca per essere il modo migliore in pratica, almeno fino a quando non Optional.stream
viene aggiunta (se mai lo fa).
AGGIORNAMENTO: Optional.stream
è stato aggiunto a JDK 9.
.flatMap(Optional::toStream)
, con la tua versione in realtà vedi cosa sta succedendo.