Dlaczego opcja Opcjonalna Java 8 nie powinna być używana w argumentach


392

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 Optionals, 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 Optionals 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 Optionalzamierzali go używać tylko jako typ zwrotu, ale nie mogę znaleźć żadnych logicznych powodów, aby nie używać go w tym scenariuszu).


16
Jeśli używałeś opcjonalnych opcji, czy nie musiałbyś sprawdzać, czy opcjonalny przekazany jako parametr nie jest null?
biziclop,

17
Tak, ale sprawiłoby, że ktoś utrzymałby kod w przyszłości, że parametr może być pusty / pusty, dlatego potencjalnie można uniknąć wyjątku wskaźnika zerowego w przyszłości
Neil Stevens

1
Łał. Brak argumentów przekazywanych do metody? To takie Visual Basic. Jaką zasadę to narusza? SRP (może). Narusza to także inną zasadę, której nazwa pozostawia mnie w osłupieniu, przekazując TYLKO niezbędne informacje dla metody lub funkcji do wykonania jej zadania.
Luminous,

20
Wszystko, co teoretycznie jest zerowe, jest jak każda metoda w bibliotece, która prawdopodobnie wywołuje System.exit (0). Nie możesz sprawdzić przeciwko tis i nie powinieneś sprawdzać przeciwko temu. Wszystko, co musiałbyś robić przez cały czas, którego tak naprawdę powinieneś (prawie) nigdy nie robić. Jak ostateczne ustawienie wszystkich parametrów. Pozwól, aby Twoje narzędzia zapobiegły zmianie wartości parametrów lub zapomnieniu o inicjalizacji pól, zamiast uniemożliwić odczytanie kodu przez tysiące finałów i tyle samo sprawdzeń zerowych.
yeoman

6
W rzeczywistości wystarczy użyć adnotacji NonNull / Nullable, to jest to, czego szukasz w tej sytuacji, a nie opcjonalne.
Bill K

Odpowiedzi:


202

Och, te style kodowania należy wziąć z odrobiną soli.

  1. (+) Przekazywanie wyniku opcjonalnego do innej metody, bez jakiejkolwiek analizy semantycznej; pozostawienie tej metody, jest całkiem w porządku.
  2. (-) Korzystanie z parametrów opcjonalnych powodujących logikę warunkową wewnątrz metod jest dosłownie przeciwwskazane.
  3. (-) Konieczność spakowania argumentu w Opcjonalnym, nie jest optymalna dla kompilatora i powoduje niepotrzebne zawijanie.
  4. (-) W porównaniu z parametrami zerowalnymi Opcjonalne jest bardziej kosztowne.

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.


38
W rzeczywistości posiadanie Optionalparametru reprezentuje jeden z trzech stanów: nullOpcjonalny, niepuste Optionalz isPresent() == falsei niepuste Optionalzawijanie wartości rzeczywistej.
biziclop,

16
@biziclop tak, krytyka była nieunikniona. Ale intencją jest, aby mieć tylko wyrażenia inne niż null. To, że nie trwało długo, aby usłyszeć radę, aby unikać Opcjonalnego w niektórych przypadkach, jest dość ironiczne. @NotNull Optional.
Joop Eggen,

80
@biziclop Zauważ, że jeśli Optionalw ogóle używasz , to stan 1 ( nullOpcjonalnie) 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)
253751

50
„nieoptymalny dla kompilatora”, „W porównaniu z parametrami dopuszczającymi wartości zerowe Opcjonalne jest bardziej kosztowne” - argumenty te mogą dotyczyć języka C, a nie języka Java. Programiści Java powinni skupić się na czystym kodzie, przenośności, testowalności, dobrej architekturze, modułowości itp., A nie na „Opcjonalne jest droższe niż zerowe odwołanie”. A jeśli okaże się, że musisz skupić się na mikrooptymalizacjach, lepiej pomiń Obiekty, Listy, Generyczne i przejdź do tablic i prymitywów (nie chcę tutaj być obraźliwy, po prostu dzielę się swoją opinią) .
Kacper86,

15
„suboptymalny dla kompilatora”: Przedwczesna optymalizacja jest źródłem wszelkiego zła ... jeśli nie piszesz kodu krytycznego pod względem wydajności (co często ma miejsce przy próbie określenia czystego interfejsu), nie powinno to stanowić problemu.
Mohan

165

Najlepiej po Widziałem na ten temat został napisany przez Daniel Olszewski :

Chociaż może być kuszące, aby rozważyć opcję opcjonalną w przypadku nieobowiązkowych parametrów metody, takie rozwiązanie jest blade w porównaniu z innymi możliwymi alternatywami. Aby zilustrować problem, sprawdź następującą deklarację konstruktora:

public SystemMessage(String title, String content, Optional<Attachment> attachment) {
    // assigning field values
}

Na pierwszy rzut oka może to wyglądać na właściwą decyzję projektową. W końcu wyraźnie zaznaczyliśmy parametr załącznika jako opcjonalny. Jednak jeśli chodzi o wywoływanie konstruktora, kod klienta może stać się nieco niezdarny.

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty());
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

Zamiast zapewnienia jasności, metody fabryczne klasy Opcjonalne tylko rozpraszają czytelnika. Zauważ, że jest tylko jeden opcjonalny parametr, ale wyobraź sobie, że masz dwa lub trzy. Wujek Bob zdecydowanie nie byłby dumny z takiego kodu 😉

Gdy metoda może zaakceptować parametry opcjonalne, lepiej jest zastosować sprawdzone podejście i zaprojektować taki przypadek przy użyciu przeciążenia metody. W przykładzie klasy SystemMessage zadeklarowanie dwóch oddzielnych konstruktorów jest lepsze niż użycie Opcjonalne.

public SystemMessage(String title, String content) {
    this(title, content, null);
}

public SystemMessage(String title, String content, Attachment attachment) {
    // assigning field values
}

Ta zmiana sprawia, że ​​kod klienta jest znacznie prostszy i łatwiejszy do odczytania.

SystemMessage withoutAttachment = new SystemMessage("title", "content");
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", attachment);

10
To dużo kopiowania i wklejania, gdy tylko jeden lub dwa akapity są istotne.
Garret Wilson,

47
Niestety, to objaśnienie nie rozwiązuje problemu, kiedy dzwoniący analizuje np. Niektóre informacje, które mogą być opcjonalne. W przypadku przeciążenia metody (zgodnie z zaleceniami powyżej) kod wywołujący musi powiedzieć: „Czy X jest obecny? Jeśli tak, wywołam przeciążoną metodę A. Czy Y występuje? Będę musiał wywołać przeciążoną metodę B. Jeśli X i Y są obecne, będę musiał wywołać przeciążoną metodę C. ” I tak dalej. Być może jest na to dobra odpowiedź, ale to wyjaśnienie „dlaczego” tego nie obejmuje.
Garret Wilson

9
Ponadto w przypadku kolekcji istnieje wyraźna różnica między pustą kolekcją a brakiem kolekcji. Na przykład pamięć podręczna. Czy to była chybiona pamięć podręczna? pusty opcjonalny / null. Czy trafienie w pamięć podręczną okazało się być pustą kolekcją? pełna opcjonalna / kolekcja.
Ajax

1
@Ajax Myślę, że nie rozumiesz tego artykułu. Cytują punkt zalecany przez książkę Effective Java , która mówi, że gdy typem zwracanym metody jest Kolekcja (nie jest to dowolny obiekt, który zwróciłby pamięć podręczną), powinieneś preferować zwracanie pustej kolekcji zamiast nulllub Optional.
Gili,

4
Jasne, powinieneś go faworyzować , ale nie zawsze masz kontrolę nad jakimś kodem i, w bardzo realnym sensie, możesz chcieć rozróżnić „jest wartość i jest to pusta kolekcja” w porównaniu do „nie ma” wartość zdefiniowana (jeszcze) ". Ponieważ „magiczna wartość zerowa” jest również odradzaną praktyką, to od ciebie, programisty, zależy, czy wybierzesz najmniej złą opcję. Wolę pustą opcjonalną reprezentować brak pamięci podręcznej zamiast pustej kolekcji, ponieważ rzeczywistą wartością w pamięci podręcznej może być pusta kolekcja.
Ajax

86

Nie ma prawie żadnych dobrych powodów, aby nie używać Optionaljako 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 że Optionalargumenty 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ą Optionalparametry. Jednym z najlepszych sposobów wykorzystania tego jest posiadanie wielu opcjonalnych parametrów i liftM2uż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.


3
Jak na temat korzystania @Nonnulli @Nullableod javax.annotation zamiast? Używam ich szeroko w rozwijanej przeze mnie bibliotece, a wsparcie zapewniane przez IDE (IntelliJ) jest bardzo dobre.
Rogério,

1
To dobrze, ale musisz używać tej adnotacji wszędzie i potrzebujesz biblioteki do obsługi takich metod, jak mapa, flatMap / bind, liftM2, sekwencja itp.
Mark Perry

14
Funkcjonalne języki programowania zachęcają do parametrów opcjonalnych. Wymagany cytat. Większość funkcji nie powinna być opcjonalna; zamiast tego ciężar wywoływania (używania odpowiednich funkcji wyższego rzędu) obecności lub braku wartości spoczywa na wywołującej danej funkcji.
jub0bs

5
To. Rzeczywiście, żadna z odpowiedzi nie jest wystarczająco przekonująca i żadna z nich nie odpowiada na to konkretne pytanie „dlaczego stosowanie opcji jako parametrów metody jest uważane za złą praktykę, podczas gdy adnotacja parametrów metody @NonNulljest 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.
Utku Özdemir,

1
Oczywiście preferowane niż zero, ale jeszcze bardziej preferowane jest nie potrzebowanie wielu opcjonalnych wartości w pierwszej kolejności, ponieważ dobry projekt: D
yeoman

12

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ć Optionalargumentu:

  • chcesz, aby twoja funkcja była używana w połączeniu z innym API, który zwraca an Optional
  • Twoja funkcja powinna zwrócić coś innego niż pusty, Optionaljeśli podana wartość jest pusta
  • Myślisz Optional jest tak niesamowity, że każdy, kto korzysta z interfejsu API, powinien zostać o nim poinformowany ;-)

10

Wzór z Optionaljest dla jednego, aby uniknąć powrotu null . 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 nullwartoś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.

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}

Problem polega raczej na tym, że @NotNull nie jest domyślnym przypadkiem i musi być wyraźnie opatrzony adnotacjami - stąd podstawa i rzeczy, których ludzie po prostu nie zrobią z powodu lenistwa.
dwegener

1
@dwegener Tak, ale Optionalteż nie rozwiązuje problemu, stąd moja sugestia dotycząca adnotacji.
Makoto,

2
O wiele trudniej jest zignorować Opcjonalne niż zignorowanie adnotacji, którą zauważysz tylko, gdy czytasz dokumentację / źródło lub jeśli twoje narzędzia ją wychwytują (analiza statyczna nie zawsze może poprawnie określić nullability).
Ajax

Zauważ, że @Nullable nie może oznaczać, że „akceptujesz” null jako prawidłową wartość, tj. Nie zgłaszasz żadnego wyjątku. Może to po prostu oznaczać, że będziesz się bronił przed tą sprawą, ale nadal rzuci wyjątek (jest to semantyka Findbugs). Dzięki temu możesz wprowadzać dwuznaczności za pomocą takich adnotacji zamiast przejrzystości.
tkruse

Nie jestem pewien, jakie są niejasności @ user2986984. „Brak obsługi wartości zerowej” może jedynie realistycznie oznaczać, że został zgłoszony wyjątek.
Makoto,

7

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.


7
Czy zwracane wartości i pola również nie mogą mieć wartości NULL? Czy Optionalto całkowicie bezcelowe?
Samuel Edwin Ward,


4

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ć.


3

Innym powodem, dla którego należy zachować ostrożność przy przekazywaniu Optionalparametru jako, jest to, że metoda powinna zrobić jedną rzecz ... Jeśli zdasz Optionalparametr, możesz faworyzować wykonanie więcej niż jednej rzeczy, może być podobne do przekazania parametru boolowskiego.

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }

Myślę, że to nie jest dobry powód. Tę samą logikę można zastosować do sprawdzenia, czy parametr jest pusty, gdy nie jest używany opcjonalny. W rzeczywistości if(param.isPresent())nie jest to najlepsze podejście do korzystania Opcjonalne, zamiast tego możesz użyć:param.forEach(() -> {...})
hdkrus

Nie podoba mi się również pomysł przekazania zerowalnych parametrów. W każdym razie powiedziałem, że możesz faworyzować, oczywiście są wyjątki, których możesz użyć, to zależy od ciebie, po prostu używaj go ostrożnie, to wszystko. Nie ma żadnej reguły, która dotyczy wszystkich scenariuszy.
Pau,

2

Akceptacja Opcjonalne jako parametry powoduje niepotrzebne zawijanie na poziomie dzwoniącego.

Na przykład w przypadku:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

Załóżmy, że masz dwa łańcuchy niepuste (tzn. Zwrócone z innej metody):

String p1 = "p1"; 
String p2 = "p2";

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 :

Either<Error, String> value = compute().right().map((s) -> calculateSomething(
< here you have to wrap the parameter in a Optional even if you know it's a 
  string >));

ref:

metody nie powinny oczekiwać opcji jako parametrów, prawie zawsze jest to zapach kodu, który wskazywał na wyciek przepływu kontrolnego od dzwoniącego do odbierającego, to na dzwoniącym powinna spoczywać odpowiedzialność za sprawdzenie zawartości opcji

ref. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749


1

Myślę, że dzieje się tak dlatego, że zwykle piszesz swoje funkcje w celu manipulowania danymi, a następnie przestawiasz je na Optionalkorzystanie z mappodobnych funkcji. Dodaje to domyślne Optionalzachowanie. Oczywiście mogą zdarzyć się przypadki, w których konieczne będzie napisanie własnej funkcji pomocniczej, która działa Optional.


1

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.


2
Przekazywanie wartości zerowej Opcjonalne odniesienie można uznać za błąd programowy (ponieważ Opcjonalny zapewnia jawny sposób przekazywania „pustej” wartości), więc nie warto go sprawdzać, chyba że chcesz uniknąć zgłaszania wyjątków we wszystkich przypadkach.
pvgoran

1

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.


5
Przykro mi, ale nie mogę zgodzić się z argumentem „nie został zaprojektowany” lub „ktoś poleca to”. Inżynier powinien być konkretny. Używanie @Nullable jest znacznie gorsze niż używanie Opcjonalne, ponieważ Opcjonalne są bardziej szczegółowe z punktu widzenia API. Nie widzę żadnych dobrych powodów, aby nie używać Opcjonalnych jako argumentów (pod warunkiem, że nie chcesz używać przeciążania metod)
Kacper86,

0

Jeszcze jedno podejście, co możesz zrobić

// get your optionals first
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

// bind values to a function
Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

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ą

calculatedValueSupplier.apply();

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.


0

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:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();

0

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.


0

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.


0

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 jak Optional.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:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) {
    return t -> Optional.ofNullable(function.apply(t));
}

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:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

jest czystszy niż to:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

1
Cóż, to JEDEN z powodów, dla których został dodany do Javy. Jest wiele innych. Zastępowanie długich łańcuchów zagnieżdżonych instrukcji „if”, które przechodzą do struktury danych, pojedynczą sekwencją połączeń łańcuchowych do Optional.map jest moim osobistym ulubionym. Świat programowania Functional Languages ​​ma wiele interesujących zastosowań Opcjonalnych, poza tym po prostu zastępując takie kontrole zerowe.
Some Guy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.