Przeczytałem na wielu stronach internetowych Opcjonalne powinny być używane tylko jako typ zwracany, a nie używane w argumentach metod. Próbuję znaleźć logiczny powód. Na przykład mam logikę, która ma 2 opcjonalne parametry. Dlatego myślę, że sensowne byłoby napisanie mojej sygnatury metody w ten sposób (rozwiązanie 1):
public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
// my logic
}
Wiele stron internetowych określa Opcjonalne nie należy używać jako argumentów metody. Mając to na uwadze, mógłbym użyć poniższej sygnatury metody i dodać wyraźny komentarz Javadoc, aby określić, że argumenty mogą być zerowe, mając nadzieję, że przyszli opiekunowie przeczytają Javadoc, a zatem zawsze przeprowadzają kontrole zerowe przed użyciem argumentów (rozwiązanie 2) :
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Alternatywnie mógłbym zastąpić moją metodę czterema publicznymi metodami, aby zapewnić lepszy interfejs i uczynić go bardziej oczywistym, p1 i p2 są opcjonalne (rozwiązanie 3):
public int calculateSomething() {
calculateSomething(null, null);
}
public int calculateSomething(String p1) {
calculateSomething(p1, null);
}
public int calculateSomething(BigDecimal p2) {
calculateSomething(null, p2);
}
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Teraz próbuję napisać kod klasy, który wywołuje tę logikę dla każdego podejścia. Najpierw pobieram dwa parametry wejściowe z innego obiektu, który zwraca Optional
s, a następnie wywołuję calculateSomething
. Dlatego jeśli zastosowane zostanie rozwiązanie 1, kod wywołujący wyglądałby następująco:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);
w przypadku użycia rozwiązania 2 kod wywołujący wyglądałby następująco:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
jeśli zastosowane zostanie rozwiązanie 3, mógłbym użyć powyższego kodu lub użyć następującego (ale jest to znacznie więcej kodu):
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
if (p2.isPresent()) {
result = myObject.calculateSomething(p1, p2);
} else {
result = myObject.calculateSomething(p1);
}
} else {
if (p2.isPresent()) {
result = myObject.calculateSomething(p2);
} else {
result = myObject.calculateSomething();
}
}
Więc moje pytanie brzmi: dlaczego uważa się za złą praktykę stosowanie Optional
s jako argumentów metody (patrz rozwiązanie 1)? Wydaje mi się to najbardziej czytelnym rozwiązaniem i sprawia, że przyszłym opiekunom staje się oczywiste, że parametry mogą być puste / zerowe. (Wiem, że projektanci Optional
zamierzali go używać tylko jako typ zwrotu, ale nie mogę znaleźć żadnych logicznych powodów, aby nie używać go w tym scenariuszu).
null
?Odpowiedzi:
Och, te style kodowania należy wziąć z odrobiną soli.
Ogólnie: Opcjonalnie jednoczy dwa stany, które należy rozwikłać. Dlatego lepiej nadaje się do wyników niż danych wejściowych, ze względu na złożoność przepływu danych.
źródło
Optional
parametru reprezentuje jeden z trzech stanów:null
Opcjonalny, niepusteOptional
zisPresent() == false
i niepusteOptional
zawijanie wartości rzeczywistej.@NotNull Optional
.Optional
w ogóle używasz , to stan 1 (null
Opcjonalnie) zwykle wskazuje na błąd programowania, więc równie dobrze możesz go nie obsłużyć (po prostu pozwól mu rzucićNullPointerException
)Najlepiej po Widziałem na ten temat został napisany przez Daniel Olszewski :
źródło
null
lubOptional
.Nie ma prawie żadnych dobrych powodów, aby nie używać
Optional
jako parametrów. Argumenty przeciwko temu opierają się na argumentach władzy (patrz Brian Goetz - jego argumentem jest to, że nie możemy egzekwować opcjonalnych wartości zerowych) lub żeOptional
argumenty mogą być zerowe (zasadniczo ten sam argument). Oczywiście każde odniesienie w Javie może być puste, musimy zachęcać do egzekwowania reguł przez kompilator, a nie pamięć programistów (co jest problematyczne i nie skaluje się).Funkcjonalne języki programowania wspierają
Optional
parametry. Jednym z najlepszych sposobów wykorzystania tego jest posiadanie wielu opcjonalnych parametrów iliftM2
używanie funkcji przy założeniu, że parametry nie są puste i zwracanie opcjonalnej (patrz http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/ data / Option.html # liftM2-fj.F- ). Java 8 niestety zaimplementowała bardzo ograniczoną bibliotekę obsługującą opcjonalnie.Jako programiści Java powinniśmy używać wartości null do interakcji ze starszymi bibliotekami.
źródło
@Nonnull
i@Nullable
od javax.annotation zamiast? Używam ich szeroko w rozwijanej przeze mnie bibliotece, a wsparcie zapewniane przez IDE (IntelliJ) jest bardzo dobre.@NonNull
jest całkowicie w porządku, jeśli służą one temu samemu celowi na różne sposoby?” Dla mnie jest tylko jeden argument, który ma co najmniej pewien sens: „Opcjonalne powinno być użyte w celu zapewnienia lepszych interfejsów API, które mają wyraźny typ zwracany. Jednak w przypadku argumentów metod można użyć przeciążonych funkcji”. - nadal nie sądzę, że jest to argument wystarczająco silny.Ta rada jest wariantem zasady „bądź tak niesprecyzowany, jak to możliwe w odniesieniu do danych wejściowych i tak konkretny, jak to możliwe w odniesieniu do wyników”.
Zwykle, jeśli masz metodę, która przyjmuje zwykłą wartość inną niż null, możesz ją odwzorować na
Optional
, więc zwykła wersja jest bardziej niespecyficzna w odniesieniu do danych wejściowych. Istnieje jednak kilka możliwych powodów, dla których warto wymagaćOptional
argumentu:Optional
Optional
jeśli podana wartość jest pustaMyśliszOptional
jest tak niesamowity, że każdy, kto korzysta z interfejsu API, powinien zostać o nim poinformowany ;-)źródło
Wzór z
Optional
jest dla jednego, aby uniknąć powrotunull
. Nadal jest całkowicie możliwe do przejścianull
zastosować metodę.Chociaż nie są jeszcze tak naprawdę oficjalne, możesz użyć adnotacji w stylu JSR-308, aby wskazać, czy akceptujesz
null
wartości w funkcji. Pamiętaj, że musisz mieć odpowiednie narzędzia, aby go faktycznie zidentyfikować, i zapewniłoby to więcej kontroli statycznej niż egzekwowalne zasady działania, ale to by pomogło.źródło
Optional
też nie rozwiązuje problemu, stąd moja sugestia dotycząca adnotacji.Wydaje mi się to trochę głupie, ale jedynym powodem, dla którego mogę wymyślić, jest to, że argumenty obiektowe w parametrach metody są już opcjonalne - mogą być zerowe. Dlatego zmuszanie kogoś do wzięcia istniejącego obiektu i zawinięcia go w opcjonalny jest w pewnym sensie bezcelowe.
To powiedziawszy, łączenie metod, które przyjmują / zwracają opcje, jest rozsądną rzeczą do zrobienia, np. Może monada.
źródło
Optional
to całkowicie bezcelowe?Sprawdź JavaDoc w JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html , dodano notatkę API:
źródło
Uważam, że Opcjonalna powinna być monada i nie można ich sobie wyobrazić w Javie.
W programowaniu funkcjonalnym masz do czynienia z funkcjami czystego i wyższego rzędu, które przyjmują i komponują ich argumenty tylko na podstawie ich „typu domeny biznesowej”. Komponowanie funkcji, które się żywią lub których obliczenia należy zgłaszać, w świecie rzeczywistym (tzw. Skutki uboczne) wymaga zastosowania funkcji, które zajmują się automatycznym rozpakowywaniem wartości z monad reprezentujących świat zewnętrzny (stan, konfiguracja, Futures, może, albo, pisarz, itp ...); nazywa się to podnoszeniem. Możesz myśleć o tym jako o oddzieleniu obaw.
Mieszanie tych dwóch poziomów abstrakcji nie ułatwia czytelności, więc lepiej jest po prostu tego unikać.
źródło
Innym powodem, dla którego należy zachować ostrożność przy przekazywaniu
Optional
parametru jako, jest to, że metoda powinna zrobić jedną rzecz ... Jeśli zdaszOptional
parametr, możesz faworyzować wykonanie więcej niż jednej rzeczy, może być podobne do przekazania parametru boolowskiego.źródło
if(param.isPresent())
nie jest to najlepsze podejście do korzystania Opcjonalne, zamiast tego możesz użyć:param.forEach(() -> {...})
Akceptacja Opcjonalne jako parametry powoduje niepotrzebne zawijanie na poziomie dzwoniącego.
Na przykład w przypadku:
Załóżmy, że masz dwa łańcuchy niepuste (tzn. Zwrócone z innej metody):
Jesteś zmuszony owinąć je opcjonalnie, nawet jeśli wiesz, że nie są puste.
Sytuacja staje się jeszcze gorsza, gdy trzeba komponować z innymi „mapowalnymi” strukturami, tj. Albo :
ref:
ref. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749
źródło
Myślę, że dzieje się tak dlatego, że zwykle piszesz swoje funkcje w celu manipulowania danymi, a następnie przestawiasz je na
Optional
korzystanie zmap
podobnych funkcji. Dodaje to domyślneOptional
zachowanie. Oczywiście mogą zdarzyć się przypadki, w których konieczne będzie napisanie własnej funkcji pomocniczej, która działaOptional
.źródło
Uważam, że rezon istnienia polega na tym, że najpierw musisz sprawdzić, czy Opcjonalny sam jest pusty, a następnie spróbować ocenić wartość, którą otacza. Zbyt wiele niepotrzebnych walidacji.
źródło
Opcje nie są zaprojektowane do tego celu, jak to dobrze wyjaśnił Brian Goetz .
Zawsze możesz użyć @Nullable, aby wskazać, że argument metody może mieć wartość NULL. Użycie opcjonalnego tak naprawdę nie pozwala na dokładniejsze napisanie logiki metody.
źródło
Jeszcze jedno podejście, co możesz zrobić
Po zbudowaniu funkcji (w tym przypadku dostawcy) będzie można przekazać ją jak każdą inną zmienną i będzie można ją wywołać za pomocą
Pomysł, czy masz opcjonalną wartość, będzie wewnętrznym szczegółem twojej funkcji i nie będzie w parametrze. Myślenie działa, gdy myślę o opcjonalnym jako parametrze, w rzeczywistości jest bardzo przydatną techniką, którą znalazłem.
Twoje pytanie, czy powinieneś to zrobić, czy nie, zależy od twoich preferencji, ale jak powiedzieli inni, sprawia, że interfejs API jest brzydki co najmniej.
źródło
Na początku wolałem również przekazać Opcjonalne jako parametr, ale jeśli przejdziesz z perspektywy API-Designer do perspektywy API-User, zobaczysz wady.
Na przykład, gdzie każdy parametr jest opcjonalny, sugerowałbym zmianę metody obliczania na własną klasę, jak poniżej:
źródło
Wiem, że to pytanie dotyczy bardziej opinii niż twardych faktów. Ale niedawno przeniosłem się z programisty .net do java, więc dopiero niedawno dołączyłem do partii opcjonalnej. Wolałbym również podać to jako komentarz, ale ponieważ mój poziom punktowy nie pozwala mi na komentowanie, jestem zmuszony podać to jako odpowiedź.
To, co robiłem, posłużyło mi jako ogólna zasada. Używa Opcjonalnych typów zwrotów i używa Opcjonalnych tylko jako parametrów, jeśli wymagam zarówno wartości Opcjonalnej, jak i pogody, czy Opcjonalna ma wartość w ramach metody.
Jeśli zależy mi tylko na wartości, sprawdzam isPresent przed wywołaniem metody, czy w metodzie mam pewien rodzaj logowania lub inną logikę, która zależy od tego, czy wartość istnieje, a następnie z radością przekażę Opcjonalne.
źródło
Wynika to z faktu, że mamy różne wymagania względem użytkownika interfejsu API i programisty interfejsu API.
Deweloper jest odpowiedzialny za zapewnienie dokładnej specyfikacji i prawidłowego wdrożenia. Dlatego jeśli programista już wie, że argument jest opcjonalny, implementacja musi sobie z nim poradzić poprawnie, niezależnie od tego, czy jest to wartość pusta, czy opcjonalna. Interfejs API powinien być jak najprostszy dla użytkownika, a wartość null jest najprostsza.
Z drugiej strony wynik jest przekazywany od programisty interfejsu API do użytkownika. Jednak specyfikacja jest kompletna i pełna, wciąż istnieje szansa, że użytkownik albo o niej nie wie, albo po prostu leniwie sobie z nią poradzi. W takim przypadku wynik opcjonalny zmusza użytkownika do napisania dodatkowego kodu, aby poradzić sobie z możliwym pustym wynikiem.
źródło
Przede wszystkim, jeśli używasz metody 3, możesz zastąpić ostatnie 14 wierszy kodu tym:
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
Cztery warianty, które napisałeś, to metody wygody . Powinieneś ich używać tylko wtedy, gdy są wygodniejsze. To także najlepsze podejście. W ten sposób API jest bardzo jasne, którzy członkowie są potrzebni, a którzy nie. Jeśli nie chcesz pisać czterech metod, możesz wyjaśnić, jak nazywasz swoje parametry:
public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)
W ten sposób jasne jest, że wartości null są dozwolone.
Twoje użycie
p1.orElse(null)
pokazuje, jak pełny jest nasz kod podczas korzystania z Opcjonalnego, co jest częścią tego, dlaczego go unikam. Opcjonalnie napisano dla programowania funkcjonalnego. Strumienie tego potrzebują. Twoje metody prawdopodobnie nigdy nie powinny zwracać Opcjonalne, chyba że konieczne jest użycie ich w programowaniu funkcjonalnym. Istnieją metody, takie jakOptional.flatMap()
metoda, które wymagają odwołania do funkcji zwracającej Opcjonalne. Oto jego podpis:public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)
Jest to zazwyczaj jedyny dobry powód do napisania metody zwracającej Opcjonalne. Ale nawet tam można tego uniknąć. Możesz przekazać moduł pobierający, który nie zwraca Opcjonalnie do metody takiej jak flatMap (), zawijając go w innej metodzie, która konwertuje funkcję na odpowiedni typ. Metoda otoki wygląda następująco:
Załóżmy, że masz taki getter:
String getName()
Nie możesz przekazać go do flatMap tak:
opt.flatMap(Widget::getName) // Won't work!
Ale możesz przekazać to w ten sposób:
opt.flatMap(optFun(Widget::getName)) // Works great!
Poza programowaniem funkcjonalnym należy unikać opcji.
Brian Goetz powiedział to najlepiej, gdy powiedział:
Powodem, dla którego opcjonalnie dodano Java, jest to, że:
jest czystszy niż to:
źródło