Dlaczego to rzuca java.lang.NullPointerException
?
List<String> strings = new ArrayList<>();
strings.add(null);
strings.add("test");
String firstString = strings.stream()
.findFirst() // Exception thrown here
.orElse("StringWhenListIsEmpty");
//.orElse(null); // Changing the `orElse()` to avoid ambiguity
Pierwsza pozycja strings
to null
, co jest wartością całkowicie akceptowalną. Ponadto findFirst()
zwraca parametr Optional , co ma jeszcze większy sens, findFirst()
aby móc obsługiwać null
s.
EDYCJA: zaktualizowano, orElse()
aby był mniej niejednoznaczny.
java
java-8
java-stream
optional
neverendingqs
źródło
źródło
String
tutaj, co jeśli jest to lista reprezentująca kolumnę w bazie danych? Wartość pierwszego wiersza dla tej kolumny może wynosićnull
.null
, ogólnie mówiąc , jest całkowicie akceptowalną wartością w Javie. W szczególności jest to prawidłowy element dla plikuArrayList<String>
. Jednak jak każda inna wartość istnieją ograniczenia dotyczące tego, co można z nią zrobić. „Nigdy nie używajnull
” nie jest przydatną radą, ponieważ nie można jej uniknąć.findFirst()
, nie chcesz już nic robić.Odpowiedzi:
Powodem tego jest użycie
Optional<T>
w zwrocie. Opcjonalne nie może zawieraćnull
. Zasadniczo nie umożliwia rozróżnienia sytuacji „nie ma go” i „jest, ale jest ustawione nanull
”.Dlatego dokumentacja jednoznacznie zabrania sytuacji, w której
null
wybranofindFirst()
:źródło
Optional
. W każdym razie myślę, że graniczę z rantowaniem - jeśli język tego nie obsługuje, to nie obsługuje.boolean
do rozróżnienia tych dwóch sytuacji miałoby sens. Wydaje mi się, że użycieOptional<T>
tutaj było wątpliwym wyborem.Iterable
typu, sprawdzahasNext()
i zwraca odpowiednią wartość.findFirst
zwraca pustą wartość opcjonalną w przypadku POJak już wspomniano , projektanci API nie zakładają, że programista chce traktować
null
wartości i wartości nieobecne w ten sam sposób.Jeśli nadal chcesz to zrobić, możesz to zrobić jawnie, stosując sekwencję
do strumienia. Wynik będzie pusty opcjonalny w obu przypadkach, jeśli nie ma pierwszego elementu lub jeśli pierwszy element jest
null
. Więc w twoim przypadku możesz użyćString firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(null);
aby uzyskać
null
wartość, jeśli pierwszy element jest nieobecny lubnull
.Jeśli chcesz rozróżnić te przypadki, możesz po prostu pominąć
flatMap
krok:Optional<String> firstString = strings.stream() .map(Optional::ofNullable).findFirst().orElse(null); System.out.println(firstString==null? "no such element": firstString.orElse("first element is null"));
Nie różni się to zbytnio od zaktualizowanego pytania. Po prostu trzeba wymienić
"no such element"
z"StringWhenListIsEmpty"
i"first element is null"
znull
. Ale jeśli nie lubisz warunków warunkowych, możesz to osiągnąć również w następujący sposób:String firstString = strings.stream().skip(0) .map(Optional::ofNullable).findFirst() .orElseGet(()->Optional.of("StringWhenListIsEmpty")) .orElse(null);
Teraz
firstString
będzie,null
jeśli element istnieje, ale jest,null
i będzie,"StringWhenListIsEmpty"
gdy żaden element nie istnieje.źródło
null
albo 1) pierwszy element,null
albo 2) brak elementów na liście. Zaktualizowałem pytanie, aby usunąć niejednoznaczność.Optional
można przypisać donull
. PonieważOptional
ma być „typem wartości”, nigdy nie powinien być pusty. I nigdy nie należy porównywać opcji==
. Kod może się nie powieść w Javie 10 :) lub kiedy typ wartości zostanie wprowadzony do Javy.null
i chociaż instancje nigdy nie powinny być porównywane==
, można przetestować odwołanie pod kątemnull
użycia,==
ponieważ jest to jedyny sposób, aby go przetestowaćnull
. Nie rozumiem, jak takie przejście na „nigdynull
” powinno działać dla istniejącego kodu, ponieważ nawet domyślna wartość dla wszystkich zmiennych instancji i elementów tablicy tonull
. Fragment z pewnością nie jest najlepszym kodem, ale nie jest też zadaniem traktowanianull
s jako wartości bieżących.null
. Jednakże, ponieważ taka hipotetyczna zmiana języka spowodowałaby, że kompilator wystawiłby tutaj błąd (nie łamałby kodu po cichu), mogę żyć z faktem, że prawdopodobnie musiałby być dostosowany do Javy 10. Przypuszczam, żeStream
API będzie wtedy też wyglądają zupełnie inaczej…Możesz użyć
java.util.Objects.nonNull
do filtrowania listy przed znalezieniemcoś jak
źródło
firstString
być,null
jeśli pierwszy przedmiotstrings
jestnull
.Optional.of
co nie jest null bezpieczne. Możesz tomap
zrobić,Optional.ofNullable
a następnie użyć,findFirst
ale skończysz z Opcjonalnym lub opcjonalnymNastępujący tekst zastępuje kod
findFirst()
zlimit(1)
i zastępujeorElse()
zreduce()
:String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
pozwala dosięgnąć tylko 1 elementureduce
.BinaryOperator
Przeszedł doreduce
deklaracji, że 1 elementem albo"StringWhenListIsEmpty"
jeśli żadne elementy osiągnąreduce
.Piękno tego rozwiązania polega na tym, że
Optional
nie jest ono przydzielone, aBinaryOperator
lambda niczego nie przydzieli.źródło
Opcjonalny ma być typem „wartości”. (czytaj wydruk grzywny w javadoc :) JVM mogą nawet zastąpić wszystko
Optional<Foo>
z tylkoFoo
, usuwając wszelkie koszty bokserskie i unboxing.null
Foo oznacza pustyOptional<Foo>
.Możliwy jest projekt zezwalający na Optional z wartością null, bez dodawania flagi boolowskiej - wystarczy dodać obiekt wartownika. (może nawet służyć
this
jako wartownik; zobacz Throwable.cuse)Decyzja, że Optional nie może zawijać wartości null, nie jest oparta na koszcie czasu wykonywania. To był bardzo sporny problem i musisz przeszukać listy mailingowe. Decyzja nie jest przekonująca dla wszystkich.
W każdym razie, ponieważ Optional nie może zawijać wartości null, popycha nas w róg w przypadkach takich jak
findFirst
. Musieli rozumować, że wartości null są bardzo rzadkie (uważano nawet, że Stream powinien blokować wartości null), dlatego wygodniej jest zgłosić wyjątek do wartości null zamiast do pustych strumieni.Sposób obejścia problemu to box
null
, npclass Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();
(Mówią, że rozwiązaniem każdego problemu OOP jest wprowadzenie innego typu :)
źródło
Box
typu. SamOptional
typ może temu służyć. Zobacz moją odpowiedź jako przykład.null
jest to ważna wartość, jak każda inna, bez specjalnego traktowania. (do pewnego czasu później :)