Dodaję drugą odpowiedź na podstawie proponowanej edycji przez użytkownika srborlongan do mojej drugiej odpowiedzi . Myślę, że zaproponowana technika była interesująca, ale nie nadawała się do edycji mojej odpowiedzi. Inni zgodzili się, a proponowana zmiana została odrzucona. (Nie byłem jednym z głosujących.) Ta technika ma jednak swoje zalety. Byłoby najlepiej, gdyby srborlongan opublikował własną odpowiedź. To się jeszcze nie zdarzyło i nie chciałem, aby technika zaginęła we mgle StackOverflow, odrzuciła historię edycji, więc postanowiłem przedstawić ją jako osobną odpowiedź.
Zasadniczo technika polega na użyciu niektórych Optional
metod w sprytny sposób, aby uniknąć konieczności używania operatora trójskładnikowego ( ? :
) lub instrukcji if / else.
Mój wbudowany przykład zostałby przepisany w ten sposób:
Optional<Other> result =
things.stream()
.map(this::resolve)
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.findFirst();
Mój przykład wykorzystujący metodę pomocniczą zostałby przepisany w następujący sposób:
/**
* 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();
KOMENTARZ
Porównajmy bezpośrednio oryginalne i zmodyfikowane wersje:
// original
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
// modified
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
Oryginał jest prosty, jeśli roboczy podejście: otrzymujemy Optional<Other>
; jeśli ma wartość, zwracamy strumień zawierający tę wartość, a jeśli nie ma żadnej wartości, zwracamy pusty strumień. Całkiem proste i łatwe do wyjaśnienia.
Modyfikacja jest sprytna i ma tę zaletę, że pozwala uniknąć warunków warunkowych. (Wiem, że niektórzy ludzie nie lubią trójskładnikowego operatora. W przypadku niewłaściwego użycia, kod może być trudny do zrozumienia). Czasami jednak rzeczy mogą być zbyt sprytne. Zmodyfikowany kod zaczyna się również od Optional<Other>
. Następnie wywołuje, Optional.map
co jest zdefiniowane następująco:
Jeśli wartość jest obecna, zastosuj do niej podaną funkcję odwzorowania, a jeśli wynik nie jest pusty, zwróć Opcjonalne opisujące wynik. W przeciwnym razie zwróć puste Opcjonalne.
map(Stream::of)
Wywołanie zwraca Optional<Stream<Other>>
. Jeśli wartość wejściowa była obecna Opcjonalnie, zwrócona Opcjonalnie zawiera strumień, który zawiera pojedynczy wynik Inne. Ale jeśli wartość nie była obecna, wynikiem jest pusta opcja.
Następnie wywołanie orElseGet(Stream::empty)
zwraca wartość typu Stream<Other>
. Jeśli jego wartość wejściowa jest obecna, otrzymuje wartość, która jest pojedynczym elementem Stream<Other>
. W przeciwnym razie (jeśli wartość wejściowa jest nieobecna) zwraca wartość pustą Stream<Other>
. Tak więc wynik jest poprawny, taki sam jak oryginalny kod warunkowy.
W komentarzach do mojej odpowiedzi dotyczącej odrzuconej edycji opisałem tę technikę jako „bardziej zwięzłą, ale także bardziej niejasną”. Stoję przy tym. Zajęło mi trochę czasu, aby dowiedzieć się, co robi, a także zajęło mi trochę czasu, aby napisać powyższy opis tego, co robi. Kluczową subtelnością jest transformacja z Optional<Other>
na Optional<Stream<Other>>
. Kiedy już to zrozumiesz, ma to sens, ale nie było dla mnie oczywiste.
Przyznaję jednak, że rzeczy, które początkowo są niejasne, z czasem mogą stać się idiomatyczne. Może się zdarzyć, że ta technika stanie się najlepszym sposobem w praktyce, przynajmniej do czasu Optional.stream
dodania (jeśli w ogóle).
AKTUALIZACJA: Optional.stream
dodano do JDK 9.
.flatMap(Optional::toStream)
, w twojej wersji faktycznie widzisz, co się dzieje.