Co jest opcjonalne?
Parametr Opcjonalny służy do przenoszenia obiektów i umożliwia obsługę odwołań o wartości null przez różne interfejsy API. Spójrzmy na fragment kodu:Coffee coffee = new Coffee();
Integer quantity = coffee.getSugar().getQuantity();
Mamy instancję Coffee , w której pobieramy trochę cukru z instancji obiektu Sugar . Jeśli założymy, że w konstruktorze Coffee nigdy nie została ustawiona wartość ilości , to funkcja Coffee.getSugar().getQuantity() zwróci wyjątek NullPointerException . Oczywiście zawsze możemy użyć starych, dobrych kontroli zerowych , aby rozwiązać problem.
Coffee coffee = new Coffee();
Integer quantity = 0;
if (coffee.getSugar() != null) {
quantity = coffee.getSugar().getQuantity();
}
Teraz wszystko wydaje się być w porządku. Ale pisząc kod Java, lepiej unikać stosowania kontroli zerowych . Zobaczmy, jak można to zrobić za pomocą Opcjonalnie.
Jak utworzyć Opcjonalne
Istnieją trzy sposoby tworzenia obiektów opcjonalnych:-
of(T value) — utworzenie opcjonalnego obiektu o wartości innej niż null. Należy pamiętać, że użycie of() w odniesieniu do obiektu o wartości null spowoduje wygenerowanie wyjątku NullPointerException .
-
ofNullable(wartość T) - tworzy wartość opcjonalną dla obiektu, który może mieć wartość null.
-
pusty() — Tworzy opcjonalną instancję, która reprezentuje odwołanie do null .
// пример использования Optional.of(T Value)
String name = "foo";
Optional<String> stringExample = Optional.of(name)
// пример использования Optional.ofNullable(T Value)
Integer age = null;
Optional<Integer> integerExample= Optional.ofNullable(age)
// пример использования Optional.empty()
Optional<Object> emptyExample = Optional.empty();
Masz więc obiekt opcjonalny. Przyjrzyjmy się teraz dwóm głównym metodom Opcjonalnym:
-
isPresent() — ta metoda informuje, czy obiekt opcjonalny zawiera wartość różną od null.
-
get() — Pobiera wartość Opcjonalną z bieżącą wartością. Należy pamiętać, że wywołanie metody get() na pustym obiekcie Option spowoduje zwrócenie wyjątku NullPointerException .
Ulepszanie sprawdzania wartości zerowej za pomocą opcji
Jak zatem możemy ulepszyć powyższy kod? Dzięki Opcjonalnym możemy zrozumieć obecność obiektu za pomocą isPresent() i pobrać go za pomocą get() . Zacznijmy od spakowania wyniku kawy.getSugar() za pomocą opcji Opcjonalne i użycia metody isPresent() . Pomoże nam to ustalić, czy funkcja getSugar() zwraca wartość null.Coffee coffee = new Coffee();
Optional<String> sugar = Optional.ofNullable(coffee.getSugar());
int quantity = 0;
if (sugar.isPresent()) {
Sugar sugar = sugar.get();
int quantity = sugar.getQuantity();
}
Patrząc na ten przykład, pakowanie wyniku funkcji Coffee.getSugar() do Opcjonalnej nie wydaje się dodawać żadnej wartości, a raczej dodaje kłopotów. Wynik możemy poprawić korzystając z moich ulubionych funkcji z klasy Opcjonalne:
-
map(Function<? super T,? Extends U> mapper) - Mapuje wartość zawartą w Opcjonalne na podaną funkcję. Jeśli parametr Opcjonalny jest pusty, map() zwróci Opcjonalną.empty() .
-
orElse(T other) to „specjalna” wersja metody get() . Może uzyskać wartość zawartą w Opcjonalne. Jednakże w przypadku pustej Opcjonalnej zwróci to wartość przekazaną do metody orElse() .
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElse(0);
To jest naprawdę fajne – przynajmniej tak mi się wydaje. Jeśli teraz w przypadku pustej wartości nie chcemy zwracać wartości domyślnej, to musimy zgłosić jakiś wyjątek. orElseThrow(Dostawca<? rozszerza X> wyjątekDostawca) zwraca wartość zawartą w parametrach opcjonalnych lub zgłasza wyjątek, jeśli opcjonalny jest pusty.
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElseThrow(IllegalArgumentException::new);
Jak widać, Opcjonalne zapewnia kilka korzyści:
- streszczenia kontroli zerowych
- udostępnia interfejs API do obsługi obiektów null
- pozwala na podejście deklaratywne do wyrażenia tego, co zostało osiągnięte
Jak stać się skutecznym dzięki Opcjonalnym
W mojej pracy używam Opcjonalny jako typu zwracanego, gdy metoda może zwrócić stan „brak wyniku”. Zwykle używam go przy definiowaniu typów zwracanych metod.Optional<Coffee> findByName(String name) {
...
}
Czasami nie jest to konieczne. Na przykład, jeśli mam metodę zwracającą wartość int , taką jak getQuantity() w klasie Sugar , wówczas metoda może zwrócić 0 , jeśli wynikiem jest null , co oznacza „brak ilości”. Wiedząc o tym, możemy pomyśleć, że parametr Sugar w klasie Coffee można przedstawić jako opcjonalny. Na pierwszy rzut oka wydaje się to dobrym pomysłem, ponieważ teoretycznie cukier nie musi być obecny w kawie. Jednak w tym miejscu chciałbym się poruszyć, kiedy nie używać Opcjonalnego. Powinniśmy unikać używania Opcjonalnego w następujących scenariuszach:
-
Jako typy parametrów dla POJO , takich jak DTO . Opcji nie można serializować, więc użycie ich w POJO powoduje, że obiektu nie można serializować.
-
Jako argument metody. Jeśli argumentem metody może być null , to z perspektywy czystego kodu przekazanie wartości null jest nadal lepsze niż przekazanie Opcjonalne. Ponadto można utworzyć przeciążone metody, aby abstrakcyjnie obsłużyć brak argumentu metody o wartości null.
-
Aby reprezentować brakujący obiekt kolekcji. Kolekcje mogą być puste, więc pusta kolekcja , taka jak pusta Set lub List , musi zostać użyta do reprezentowania kolekcji bez wartości.
GO TO FULL VERSION