Pracowałem z nowym typem opcjonalnym w Javie 8 i natknąłem się na coś, co wygląda na typową operację, która nie jest obsługiwana funkcjonalnie: „orElseOptional”
Rozważ następujący wzór:
Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
Optional<Result> resultFromServiceB = serviceB(args);
if (resultFromServiceB.isPresent) return resultFromServiceB;
else return serviceC(args);
}
Istnieje wiele form tego wzorca, ale sprowadza się on do chęci użycia „orElse” na opcji, która przyjmuje funkcję produkującą nową opcję, wywoływaną tylko wtedy, gdy bieżąca nie istnieje.
Jego implementacja wyglądałaby tak:
public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
return value != null ? this : other.get();
}
Ciekaw jestem, czy istnieje powód, dla którego taka metoda nie istnieje, czy po prostu używam Optional w niezamierzony sposób i jakie inne sposoby wymyślili ludzie, aby poradzić sobie z tym przypadkiem.
Powinienem powiedzieć, że uważam, że rozwiązania obejmujące niestandardowe klasy / metody narzędziowe nie są eleganckie, ponieważ ludzie pracujący z moim kodem niekoniecznie wiedzą, że istnieją.
Poza tym, jeśli ktoś wie, czy taka metoda zostanie uwzględniona w JDK 9 i gdzie mogę zaproponować taką metodę? Wydaje mi się to dość rażącym pominięciem API.
orElseGet()
to, czego potrzebuje OP, ale nie generuje ładnej kaskadowej składni.Odpowiedzi:
Jest to część JDK 9 w postaci
or
, która ma rozszerzenieSupplier<Optional<T>>
. Twój przykład byłby wtedy:Aby uzyskać szczegółowe informacje, zobacz Javadoc lub ten post, który napisałem.
źródło
ifPresent
. W każdym razie myślę, że nazwa iifPresent
tak nie jest dobra. Dla wszystkich innych metod nie mając „else” w nazwie (jakmap
,filter
,flatMap
), to zakłada się, że nie robią nic, jeśli wartość nie jest obecna, więc dlaczegoifPresent
...Optional<T> perform(Consumer<T> c)
metody umożliwiającej łączenie w łańcuchperform(x).orElseDo(y)
(orElseDo
jako alternatywa dla proponowanejifEmpty
, aby być spójnym welse
nazwie wszystkich metod, które mogą zrobić coś dla nieobecnych wartości). Możesz to naśladowaćperform
w Javie 9,stream().peek(x).findFirst()
chociaż jest to nadużycie interfejsu API i nadal nie ma możliwości wykonania poleceniaRunnable
bez określenia aConsumer
w tym samym czasie…Najczystszym podejściem „wypróbuj usługi” przy obecnym API byłoby:
Ważnym aspektem nie jest (stały) łańcuch operacji, który trzeba raz napisać, ale to, jak łatwo jest dodać kolejną usługę (lub zmodyfikować listę usług jest ogólna). Tutaj
()->serviceX(args)
wystarczy dodać lub usunąć singiel .Ze względu na leniwą ocenę strumieni żadna usługa nie zostanie wywołana, jeśli poprzednia usługa zwróciła wartość niepustą
Optional
.źródło
.map(Optional::get)
z.findFirst()
ułatwi „czytanie”, np. Czy.filter(Optional::isPresent).findFirst().map(Optional::get)
można „czytać” jak ”znaleźć pierwszy element w strumieniu, dla którego parametr Optional :: isPresent ma wartość true, a następnie spłaszczyć go stosując Optional :: get”?To nie jest ładne, ale to zadziała:
.map(func).orElseGet(sup)
jest dość poręcznym wzorem do użytku zOptional
. To znaczy „Jeśli toOptional
zawiera wartośćv
, daj mifunc(v)
, w przeciwnym razie daj misup.get()
”.W takim przypadku dzwonimy
serviceA(args)
i otrzymujemyOptional<Result>
. JeśliOptional
zawiera wartośćv
, chcemy otrzymaćOptional.of(v)
, ale jeśli jest pusta, chcemy pobraćserviceB(args)
. Powtórz płukanie z większą liczbą alternatyw.Inne zastosowania tego wzoru to
.map(Stream::of).orElseGet(Stream::empty)
.map(Collections::singleton).orElseGet(Collections::emptySet)
źródło
() -> {}
nie zwraca plikuOptional
. Co próbujesz osiągnąć?or(Supplier<Optional<T>>)
Java 9.map()
na pustymOptional
utworzy pustyOptional
.Być może właśnie tego szukasz: Uzyskaj wartość z jednego lub drugiego opcjonalnego
W przeciwnym razie warto rzucić okiem
Optional.orElseGet
. Oto przykład tego, czego myślę , że szukasz:źródło
ofNullable
to najfajniejsza rzecz, jaką kiedykolwiek widziałem.Zakładając, że nadal korzystasz z JDK8, istnieje kilka opcji.
Opcja nr 1: stwórz własną metodę pomocniczą
Na przykład:
Abyś mógł:
Opcja nr 2: użyj biblioteki
Np. Opcja opcjonalna google guava obsługuje prawidłowe
or()
działanie (podobnie jak JDK9), np .:(Gdzie każda z usług powraca
com.google.common.base.Optional
, a niejava.util.Optional
).źródło
Optional<T>.or(Supplier<Optional<T>>)
w dokumentach Guava. Czy masz do tego link?Wygląda na to, że dobrze pasuje do dopasowywania wzorców i bardziej tradycyjnego interfejsu Option z implementacjami Some i None (takich jak te w Javaslang , FunctionalJava ) lub leniwą implementacją Może w cyclops-react. Jestem autorem tej biblioteki.
Z cyklopem-reagować możesz także używać strukturalnego dopasowywania wzorców na typach JDK. W przypadku opcji Opcjonalne można dopasować do obecnych i nieobecnych przypadków według wzorca gości . wyglądałoby to mniej więcej tak -
źródło