Niedawno czytałem o tym i widziałem, jak ludzie używają tej klasy, ale w prawie wszystkich przypadkach używanie również null
by działało - jeśli nie bardziej intuicyjnie. Czy ktoś może podać konkretny przykład, w którym można Optional
by osiągnąć coś, null
czego nie można lub w znacznie czystszy sposób? Jedyne, co przychodzi mi do głowy, to używać go z kluczami Maps
nie akceptującymi null
, ale nawet to można by zrobić z bocznym "mapowaniem" wartości null. Czy ktoś może podać mi bardziej przekonujący argument? Dziękuję Ci.
89
Odpowiedzi:
Członek zespołu Guava tutaj.
Prawdopodobnie największą wadą programu
null
jest to, że nie jest oczywiste, co to powinno oznaczać w danym kontekście: nie ma ilustracyjnej nazwy. Nie zawsze jest oczywiste, żenull
oznacza to „brak wartości dla tego parametru” - do cholery, jako wartość zwracana, czasami oznacza to „błąd”, a nawet „sukces” (!!), lub po prostu „prawidłowa odpowiedź to nic”.Optional
jest często pojęciem, które faktycznie masz na myśli, ustawiając zmienną dopuszczającą wartość null, ale nie zawsze. Jeśli tak nie jest, zalecamy napisanie własnej klasy, podobnej doOptional
innego schematu nazewnictwa, ale z innym schematem, aby wyjaśnić, co tak naprawdę masz na myśli.Ale powiedziałbym, że największą zaletą
Optional
nie jest czytelność: zaletą jest to, że jest odporny na idiotę. Zmusza cię do aktywnego myślenia o nieobecnym przypadku, jeśli chcesz, aby program w ogóle się kompilował, ponieważ musisz aktywnie rozpakowaćOptional
i zająć się tym przypadkiem. Null sprawia, że po prostu zapomnienie o rzeczach jest niepokojąco łatwe i chociaż FindBugs pomaga, nie sądzę, że rozwiązuje ten problem równie dobrze. Jest to szczególnie istotne, gdy zwracasz wartości, które mogą, ale nie muszą być „obecne”. Ty (i inni) jesteście znacznie bardziej skłonni zapomnieć, żeother.method(a, b)
może zwrócićnull
wartość, niż prawdopodobnie zapomnieć, żea
może to mieć miejscenull
podczas implementacjiother.method
. PowracającyOptional
sprawia, że wywołujący nie mogą zapomnieć o tym przypadku, ponieważ sami muszą rozpakować obiekt.Z tych powodów zalecamy używanie
Optional
jako zwracanego typu dla metod, ale niekoniecznie w argumentach metody.(Nawiasem mówiąc, jest to całkowicie zaczerpnięte z dyskusji tutaj ).
źródło
Map
powraca,null
jeśli klucz jest niezamapowany, ale pamiętaj, że jeśli to zrobiszmap.put(key, null)
,map.containsKey(key)
zwróci,true
alemap.get(key)
zwrócinull
.Optional
może być przydatny w wyjaśnianiu różnicy między przypadkiem „jawnie zamapowanym na wartość null” a przypadkiem „nieobecny na mapie”. Zgadzam się, żeOptional
można to nadużyć, ale nie jestem jeszcze przekonany, że przypadek, który opisujesz, jest nadużyciem.Optional<T>
jest to wartość „znaleziona, ale nieprawidłowa”. A dokładniej,Optional<T>
jest to sposób na udekorowanie dowolnego typuT
dodatkową wartością „znalezioną, ale nieprawidłową” - tworzenie nowego typu poprzez połączenie dwóch istniejących typów. Jeśli masz sto klas, utworzenie oddzielnej wartości „znalezionej, ale niepoprawnej” dla każdej z nichOptional<T>
może być kłopotliwe , ale może z łatwością działać dla wszystkich.Naprawdę wygląda jak
Maybe
wzór Monad od Haskella.Warto przeczytać następującą, Wikipedię Monad (programowanie funkcjonalne) :
I odczytać z opcjonalnego do Monady z guawa na Kerflyn Blog, który omawia temat opcjonalnego guawa używany jako monady:
Edycja: w Javie8 jest wbudowana opcja, która ma operatory monadyczne, takie jak
flatMap
. To był kontrowersyjny temat, ale w końcu został wdrożony.Zobacz http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html
public Optional<String> tryFindSimilar(String s) //... Optional<Optional<String>> bad = opt.map(this::tryFindSimilar); Optional<String> similar = opt.flatMap(this::tryFindSimilar);
flatMap
Operator jest niezbędna, aby umożliwić monadycznego operacji i pozwala na łatwe połączeń łańcuchowych, że wszystkie powrotne opcjonalne wyników.Pomyśl o tym, gdybyś użył
map
operatora 5 razy, skończyłbyś zOptional<Optional<Optional<Optional<Optional<String>>>>>
, a użycieflatMap
dałoby ciOptional<String>
Od Java8 wolałbym nie używać opcjonalnego Guavy, który jest mniej wydajny.
źródło
Jednym z dobrych powodów, aby go używać, jest to, że sprawia, że wartości null są bardzo znaczące. Zamiast zwracać wartość null, która może oznaczać wiele rzeczy (np. Błąd, awaria, pusta wartość itp.), Możesz wstawić „nazwę” do wartości null. Spójrz na ten przykład:
zdefiniujmy podstawowe POJO:
class PersonDetails { String person; String comments; public PersonDetails(String person, String comments) { this.person = person; this.comments = comments; } public String getPerson() { return person; } public String getComments() { return comments; }
}
Skorzystajmy teraz z tego prostego POJO:
public Optional<PersonDetails> getPersonDetailstWithOptional () { PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless, lets make the return value more meaningful*/ if (details == null) { //return an absent here, caller can check for absent to signify details are not present return Optional.absent(); } else { //else return the details wrapped in a guava 'optional' return Optional.of(details); } }
Teraz unikajmy używania null i wykonuj nasze sprawdzenia z opcją Optional, więc jest to sensowne
public void checkUsingOptional () { Optional<PersonDetails> details = getPersonDetailstWithOptional(); /*below condition checks if persons details are present (notice we dont check if person details are null, we use something more meaningful. Guava optional forces this with the implementation)*/ if (details.isPresent()) { PersonDetails details = details.get(); // proceed with further processing logger.info(details); } else { // do nothing logger.info("object was null"); } assertFalse(details.isPresent()); }
więc ostatecznie jest to sposób na nadanie zerom znaczenia i mniej dwuznaczności.
źródło
Najważniejszą zaletą Optional jest to, że dodaje więcej szczegółów do kontraktu między implementatorem a obiektem wywołującym funkcję. Z tego powodu jest przydatna zarówno dla parametrów, jak i typu zwracanego.
Jeśli przyjmiesz konwencję, aby zawsze mieć
Optional
dla możliwych obiektów zerowych, dodasz więcej wyjaśnień do przypadków, takich jak:Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)
Umowa tutaj jasno określa, że istnieje szansa, że wynik nie zostanie zwrócony, ale także pokazuje, że będzie działać z
from
ito
jako nieobecny.Optional<Integer> maxPrime(Optional<Integer> from, Integer to)
Kontrakt określa, że from jest opcjonalny, więc nieobecna wartość może mieć specjalne znaczenie, takie jak początek od 2. Mogę się spodziewać, że wartość null
to
parametru zgłosi wyjątek.Tak więc dobrą częścią korzystania z Optional jest to, że kontrakt stał się zarówno opisowy (podobnie jak w przypadku
@NotNull
adnotacji), ale także formalny, ponieważ musisz napisać kod,.get()
aby sobie z nim poradzićOptional
.źródło