Korzystam z Java 8 już od ponad 6 miesięcy, jestem całkiem zadowolony z nowych zmian API. Jednym z obszarów, w którym wciąż nie jestem pewny, jest czas użycia Optional
. Wydaje mi się, że waham się między chęcią używania go wszędzie, gdzie coś może być null
, i nigdzie.
Wydaje się, że jest wiele sytuacji, w których mógłbym go użyć i nigdy nie jestem pewien, czy to dodaje korzyści (czytelność / bezpieczeństwo zerowe), czy po prostu powoduje dodatkowe obciążenie.
Mam więc kilka przykładów i chciałbym zainteresować się przemyśleniami społeczności na temat tego, czy Optional
jest to korzystne.
1 - Jako typ zwracany przez metodę publiczną, gdy metoda może zwrócić null
:
public Optional<Foo> findFoo(String id);
2 - Jako parametr metody, gdy parametr może być null
:
public Foo doSomething(String id, Optional<Bar> barOptional);
3 - Jako opcjonalny członek fasoli:
public class Book {
private List<Pages> pages;
private Optional<Index> index;
}
4 - W Collections
:
Ogólnie nie sądzę:
List<Optional<Foo>>
dodaje cokolwiek - zwłaszcza, że można użyć filter()
do usunięcia null
wartości itp., ale czy są jakieś dobre zastosowania Optional
w kolekcjach?
Jakieś przypadki, które przegapiłem?
Map<Character, String>
. Jeśli nie ma podstawienie mogę użyć tego:Optional.ofNullable(map.get(c)).orElse(String.valueOf(c))
. Pamiętaj też, że opcja została skradziona z Guawy i ma znacznie ładniejszą składnię:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
.filter(Optional::absent)
„getOrDefault()
?Odpowiedzi:
Głównym celem
Optional
jest zapewnienie środka dla funkcji zwracającej wartość wskazującą na brak wartości zwracanej. Zobacz tę dyskusję . Dzięki temu osoba dzwoniąca może kontynuować ciąg płynnych wywołań metod.To najbardziej pasuje do przypadku nr 1 w pytaniu PO. Chociaż brak wartości jest bardziej precyzyjnym sformułowaniem niż zero, ponieważ coś takiego
IntStream.findFirst
nigdy nie może zwrócić wartości null.W przypadku użycia # 2 , przekazując opcjonalny argument do metody, można sprawić, że zadziała, ale jest raczej niezdarny. Załóżmy, że masz metodę, która pobiera ciąg, po którym następuje opcjonalny drugi ciąg. Zaakceptowanie
Optional
jako drugiego argumentu spowodowałoby kod w następujący sposób:Nawet zaakceptowanie wartości null jest przyjemniejsze:
Prawdopodobnie najlepiej jest mieć przeciążoną metodę, która akceptuje argument z jednym ciągiem i zapewnia domyślną wartość dla drugiego:
Ma to ograniczenia, ale jest o wiele ładniejsze niż którekolwiek z powyższych.
Przypadki użycia # 3 i # 4 , mające
Optional
pole klasy lub strukturę danych, są uważane za niewłaściwe użycie interfejsu API. Po pierwsze, jest to sprzeczne z głównym celem projektowym,Optional
jak podano u góry. Po drugie, nie dodaje żadnej wartości.Istnieją trzy sposoby radzenia sobie z brakiem wartości w zmiennej
Optional
: zapewnienie wartości zastępczej, wywołanie funkcji w celu podania wartości zastępczej lub zgłoszenie wyjątku. Jeśli przechowujesz dane w polu, możesz to zrobić podczas inicjalizacji lub przydziału. Jeśli dodajesz wartości do listy, jak wspomniano PO, możesz dodatkowo nie dodawać wartości, tym samym „spłaszczając” nieobecne wartości.Jestem pewien, że ktoś może wymyślić jakieś wymyślone przypadki, w których naprawdę chce przechowywać
Optional
w polu lub kolekcji, ale ogólnie najlepiej jest tego unikać.źródło
Optional
jako pola, gdy rzeczy mają być zrobione lub nie do wykonania w przypadku obecności lub braku wartości.if(foo == null)
wewnętrznej byłoby lepsze niż przechowywanie pola jakooptional
. I nie rozumiem, dlaczego jawne dzwonieniegetOptionalFoo()
wewnętrzne jest lepsze niż przechowywanie pola jakoOptional
. Ponadto, jeśli jedno pole może być,null
a jedno pole nie może byćnull
, dlaczego nie przekazać tego kompilatorowi, tworząc jeOptional
lub nieOptional
.Optional<>
ekspertów: „nie przechowujOptional<>
w terenie”. Naprawdę staram się zrozumieć sens tego zalecenia. Rozważ drzewo DOM, w którym węzeł może mieć element nadrzędny lub nie. Wydaje się, że w przypadku użycia,Node.getParent()
który powróciOptional<Node>
. Czy naprawdę mamnull
za każdym razem przechowywać i zawijać wynik? Czemu? Co kupuje mi ta dodatkowa nieefektywność poza tym, że mój kod wygląda brzydko?Optional<Node> getParent() { return Optional.ofNullable(parent); }
. To wygląda drogie, ponieważ przydzielaOptional
za każdym razem! Ale jeśli dzwoniący rozpakuje go natychmiast, obiekt jest wyjątkowo krótkotrwały i nigdy nie jest promowany z otwartej przestrzeni. Może nawet zostać wyeliminowane przez analizę ucieczki JIT. Ostateczna odpowiedź brzmi „to zależy” jak zawsze, ale użycieOptional
w polach potencjalnie powoduje nadmierne zużycie pamięci i spowalnia przechodzenie przez strukturę danych. I wreszcie myślę, że to zaśmieca kod, ale to kwestia gustu.Jestem spóźniony do gry, ale za to, co jest warte, chcę dodać moje 2 centy. Są sprzeczne z celem projektowym
Optional
, który dobrze podsumowuje odpowiedź Stuarta Marksa , ale nadal jestem przekonany o ich ważności (oczywiście).Używaj opcjonalnie wszędzie
Ogólnie
Napisałem cały post na blogu o używaniu,
Optional
ale w zasadzie sprowadza się do tego:Optional
zamiastnull
Dwa pierwsze wyjątki mogą zmniejszyć postrzegany narzut zawijania i rozpakowywania odniesień w
Optional
. Są wybierane w taki sposób, że zero nie może legalnie przekroczyć granicy z jednej instancji do drugiej.Zauważ, że prawie nigdy nie pozwoli to na
Optional
s w kolekcjach, które są prawie tak złe jaknull
s. Po prostu nie rób tego. ;)Jeśli chodzi o twoje pytania
Zalety
Wykonanie tego zmniejsza obecność
null
s w bazie kodu, chociaż ich nie eliminuje. Ale to nie jest nawet główny punkt. Istnieją inne ważne zalety:Wyjaśnia zamiar
Użycie
Optional
wyraźnie oznacza, że zmienna jest, no cóż, opcjonalna. Każdy czytelnik twojego kodu lub konsument Twojego API zostanie pobity ponad to, że może tam nic nie być i że konieczne jest sprawdzenie przed uzyskaniem dostępu do wartości.Usuwa niepewność
Bez
Optional
znaczenianull
zdarzenia jest niejasne. Może to być prawna reprezentacja stanu (patrzMap.get
) lub błąd implementacji, taki jak brakująca lub nieudana inicjalizacja.Zmienia się to dramatycznie wraz z ciągłym używaniem
Optional
. Tutaj już występowanienull
oznacza obecność błędu. (Ponieważ jeśli pozwolono by na brakującą wartość,Optional
użyto by to). To sprawia, że debugowanie wyjątku wskaźnika zerowego jest o wiele łatwiejsze, ponieważ pytanie o znaczenie tegonull
jest już udzielone.Więcej czeków zerowych
Teraz, gdy już nic nie może być
null
, można to egzekwować wszędzie. Niezależnie od tego, czy są to adnotacje, twierdzenia, czy zwykłe kontrole, nigdy nie musisz zastanawiać się, czy ten argument lub ten typ zwracany może być pusty. Nie może!Niedogodności
Oczywiście nie ma srebrnej kuli ...
Występ
Zawijanie wartości (szczególnie prymitywów) w dodatkowej instancji może obniżyć wydajność. W ciasnych pętlach może to stać się zauważalne lub nawet gorsze.
Zauważ, że kompilator może być w stanie obejść dodatkowe odniesienie dla krótkich okresów życia
Optional
s. W Javie 10 typy wartości mogą dodatkowo zmniejszyć lub usunąć karę.Serializacja
Optional
nie jest serializowalny, ale obejście nie jest zbyt skomplikowane.Niezmienność
Ze względu na niezmienność typów ogólnych w Javie niektóre operacje stają się kłopotliwe, gdy rzeczywisty typ wartości jest wpychany do argumentu typu ogólnego. Podany jest tutaj przykład (patrz „Polimorfizm parametryczny”) .
źródło
List
s. Dzieje się tak, gdy ważne jest, aby określony element znajdował się pod określonym indeksem, ale element może być obecny lub nie. Jest to wyraźnie określone przezList<Optional<T>>
typ. Jeśli z drugiej strony ważne jest tylko, które obiekty, które są elementami niektórych kolekcji, nie ma sensu.Optional
Były przekazywane do metody może byćnull
. Co zyskałeś? Teraz musisz wykonać dwa czeki. Zobacz stackoverflow.com/a/31923042/650176Optional
(co powinno mieć miejsce, jeśli chcesz przekazać to jako argument),null
nigdy nie ma wartości prawnej. Wywołanie metody z jawnym parametremnull
jest oczywiście błędem.Osobiście wolę korzystać z narzędzia IntelliJ Code Inspection Tool do używania
@NotNull
i@Nullable
sprawdzania, ponieważ są to w dużej mierze czasy kompilacji (mogą mieć pewne kontrole w środowisku wykonawczym). Ma to niższe koszty ogólne w zakresie czytelności kodu i wydajności środowiska wykonawczego. Nie jest tak rygorystyczny, jak korzystanie z Opcjonalnego, jednak ten brak dyscypliny powinien być poparty przyzwoitymi testami jednostkowymi.Działa to z Javą 5 i nie trzeba zawijać ani rozpakowywać wartości. (lub utwórz obiekty opakowania)
źródło
@Nonnull
osobiście JSR 305 - współpracuje z FindBugs;)@Nonnull @NotNull etc
Myślę, że Guava Optional i ich strona wiki dość dobrze to ujmują:
Optional
dodaje trochę narzutu, ale myślę, że jego wyraźną zaletą jest wyraźne stwierdzenie, że obiekt może być nieobecny i wymusza, aby programiści poradzili sobie z sytuacją. Zapobiega to, że ktoś zapomni ukochany!= null
czek.Biorąc przykład 2 , myślę, że pisanie kodu jest o wiele bardziej wyraźne:
niż
Dla mnie
Optional
lepiej uchwycić fakt, że nie ma karty dźwiękowej.Moje 2 ¢ o twoich punktach:
public Optional<Foo> findFoo(String id);
- Nie jestem tego pewien. Może zwrócę taki,Result<Foo>
który może być pusty lub zawieraćFoo
. Jest to podobna koncepcja, ale nie bardzoOptional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- Wolałbym @Nullable i sprawdzanie błędów, jak w odpowiedzi Petera Lawreya - patrz także ta dyskusja .Optional<Index> getIndex()
wyraźnie wskazać, że książka może nie mieć indeksu.Ogólnie starałbym się zminimalizować przekazywanie
null
. (Raz spalony ...) Myślę, że warto znaleźć odpowiednie abstrakty i wskazać innym programistom, co faktycznie reprezentuje pewna zwracana wartość.źródło
soundcard.ifPresent(System.out::println)
. WywołanieisPresent
jest semantycznie takie samo jak sprawdzanienull
.if(soundcard != null && soundcard.isPresent())
. Chociaż tworzenie interfejsu API, który zwraca Opcjonalne i może również zwracać wartość NULL, jest czymś, co mam nadzieję, że nikt nigdy tego nie zrobi.Z samouczka Oracle :
źródło
Oto dobry artykuł, który pokazuje przydatność przypadku użycia nr 1. Tam jest ten kod
przekształca się w to
przez użycie Opcjonalnego jako wartości zwracanej odpowiednich metod pobierających .
źródło
Oto interesujące zastosowanie (wierzę) do ... Testów.
Zamierzam dokładnie przetestować jeden z moich projektów i dlatego buduję twierdzenia; tylko takie rzeczy muszę zweryfikować, a inne nie.
Dlatego tworzę rzeczy do potwierdzenia i używam aser do ich weryfikacji, w następujący sposób:
W klasie NodeAssert robię to:
Co oznacza, że aser naprawdę uruchamia się tylko wtedy, gdy chcę sprawdzić etykietę!
źródło
Optional
klasa pozwala uniknąć używanianull
i stanowi lepszą alternatywę:Zachęca to programistę do sprawdzania obecności w celu uniknięcia nieprzechwyconych
NullPointerException
.Interfejs API staje się lepiej udokumentowany, ponieważ można zobaczyć, gdzie można oczekiwać wartości, które mogą być nieobecne.
Optional
zapewnia wygodny interfejs API do dalszej pracy z obiektemisPresent()
:;get()
;orElse()
;orElseGet()
;orElseThrow()
;map()
;filter()
;flatmap()
.Ponadto wiele platform aktywnie korzysta z tego typu danych i zwraca je ze swojego interfejsu API.
źródło
W Javie nie używaj ich, chyba że jesteś uzależniony od programowania funkcjonalnego.
Nie mają one miejsca jako argumenty metody (przepraszam kogoś, że pewnego dnia przekaże ci wartość opcjonalną null, a nie tylko opcję pustą).
Mają sens dla zwracanych wartości, ale zachęcają klasę klienta do ciągłego rozciągania łańcucha budującego zachowanie.
FP i łańcuchy mają niewiele miejsca w imperatywnym języku, takim jak Java, ponieważ bardzo trudno jest debugować, a nie tylko czytać. Kiedy przechodzisz do linii, nie możesz poznać stanu ani intencji programu; musisz wkroczyć, aby to rozgryźć (w kodzie, który często nie jest twój, a wiele ramek stosu jest głębokich pomimo filtrów kroków) i musisz dodać wiele punktów przerwania w dół, aby upewnić się, że może się zatrzymać w dodanym kodzie / lambdzie, zamiast po prostu chodzić po trywialnych liniach if / else / call.
Jeśli chcesz programowania funkcjonalnego, wybierz coś innego niż Java i mam nadzieję, że masz narzędzia do debugowania.
źródło
Nie sądzę, że Opcjonalny jest ogólnym substytutem metod, które potencjalnie zwracają wartości null.
Podstawowa idea: brak wartości nie oznacza, że potencjalnie będzie ona dostępna w przyszłości. Jest to różnica między findById (-1) a findById (67).
Główną informacją Opcjonalnego dla dzwoniącego jest to, że może on nie liczyć na podaną wartość, ale może on być dostępny w pewnym momencie. Może zniknie ponownie i wróci później jeszcze raz. To jest jak włącznik / wyłącznik. Masz „opcję”, aby włączyć lub wyłączyć światło. Ale nie masz opcji, jeśli nie masz światła, aby włączyć.
Uważam więc, że zbyt niechlujne jest wprowadzanie Opcjonalnych wszędzie tam, gdzie potencjalnie wcześniej zwracano wartość NULL. Nadal będę używać wartości null, ale tylko w ograniczonych obszarach, takich jak korzeń drzewa, leniwa inicjalizacja i jawne metody find.
źródło
Wydaje
Optional
jest przydatna tylko wtedy, gdy typ T w dowolnej to prymitywny typ jakint
,long
,char
, itd. Dla „prawdziwych” klas, to nie ma sensu do mnie jak można użyćnull
wartości tak.Myślę, że zostało zaczerpnięte stąd (lub z innej podobnej koncepcji językowej).
Nullable<T>
W języku C #
Nullable<T>
zostało to wprowadzone dawno temu w celu zawijania typów wartości.źródło
Optional
w rzeczywistościjava.util.Optional
.Ma podobne semantykę do niemodyfikowalne wystąpienie wzorca projektowego Iterator :
Optional
isPresent()
)get()
), jeśli odnosi się do obiektunext()
metody).Dlatego rozważ zwrócenie lub przekazanie
Optional
w kontekstach, w których wcześniej mogłeś rozważyć użycie JavyIterator
.źródło
Java SE 8 wprowadza nową klasę o nazwie java.util.Optional,
Możesz utworzyć pustą opcję opcjonalną lub opcjonalną o wartości null.
A oto Opcjonalne z wartością inną niż null:
Zrób coś, jeśli wartość jest obecna
Teraz, gdy masz obiekt opcjonalny, możesz uzyskać dostęp do dostępnych metod, aby jawnie poradzić sobie z obecnością lub brakiem wartości. Zamiast pamiętać o sprawdzeniu wartości zerowej, wykonaj następujące czynności:
Możesz użyć metody ifPresent () w następujący sposób:
źródło