Jeżeli zdecydujemy się na powszechne używanie Optional zamiast nulla, to w czasie pisania kodu wiemy w którym miejscu możemy mieć wartość pustą, a w którym nie, dzięki statycznemu typowaniu. Jeżeli zdecydujemy się używać wartości null, to wtedy cały kod będzie usiany null-checkami, bo z kodu nie wywnioskuje się, gdzie się można nulla spodziewać.
Czasem też kolekcje zwracają null i jest to zupełnie ok. Np. mapa, z której pobiera sie klucz, do którego nie ma żadnej wartości zwraca null. I znając API odpowiednio to obsługujemy. I nie ma żadnego problemu.
Kolekcje powstały przed Optionalem, dlatego go nie używają.
null czasem jest upierdliwy. Przez to, że kolekcje nie zwracają Optionala nie można np zrobić:
return mapa.getOptional(key).map(processValue).orElse(defaultValue);
Za to trzeba robić:
T value = mapa.get(key);
return value == null ? defaultValue : processValue(value);
Tutaj zysk jest niewielki, ale co się stanie, gdy processValue
też może zwrócić pustą wartość i mamy też funkcję processValue2
którą chcemy złożyć z pierwszą?
Kod konwencjonalny:
T value = mapa.get(key);
U value1 = value == null ? null : processValue(value);
V value2 = value1 == null ? null : processValue2(value1);
return value2 == null ? defaultValue : value2;
Kod z Optionalem:
return mapa.getOptional(key).flatMap(processValue).flatMap(processValue2).orElse(defaultValue);
Znacznie czytelniejsze (zwłaszcza, że nie ma zmiennych pośrednich tylko sekwencyjny ciąg wywołań; czyta się więc bez wracania, by sprawdzić co znaczą zmienne pośrednie) i trudniej się walnąć. Dodatkowo jednowyrażeniowe lambdy nie potrzebują słówka return
więc go też możemy oszczędzić.
Niestety mapy nie mają metody getOptional, więc trzeba zaimportować ją z Optionala i zamiast:
mapa.getOptional(key);
będzie:
ofNullable(mapa.get(key):