Witajcie, drodzy programiści Java,
Wiem, że temat może być trochę, in advance
ponieważ JDK8 nie jest jeszcze wydany (a przynajmniej na razie nie…), ale czytałem kilka artykułów na temat wyrażeń Lambda, a zwłaszcza części związanej z nowym API kolekcji znanym jako Stream.
Oto przykład podany w artykule Java Magazine (jest to algorytm populacji wydry ..):
Set<Otter> otters = getOtters();
System.out.println(otters.stream()
.filter(o -> !o.isWild())
.map(o -> o.getKeeper())
.filter(k -> k.isFemale())
.into(new ArrayList<>())
.size());
Moje pytanie brzmi: co się stanie, jeśli w środku wewnętrznej iteracji Seta jedna z wydr jest zerowa?
Spodziewałbym się, że zostanie wyrzucony wyjątek NullPointerException, ale może nadal tkwię w poprzednim paradygmacie programowania (niefunkcjonalnym), czy ktoś może mnie oświecić, jak należy sobie z tym poradzić?
Jeśli to naprawdę wyrzuci wyjątek NullPointerException, uważam tę funkcję za dość niebezpieczną i będzie musiała być używana tylko jak poniżej:
- Deweloper, aby upewnić się, że nie ma wartości null (może przy użyciu poprzedniego .filter (o -> o! = Null))
- Deweloper, aby upewnić się, że aplikacja nigdy nie generuje pustej wydry ani specjalnego obiektu NullOtter do obsługi.
Jaka jest najlepsza opcja, czy jakakolwiek inna opcja?
Dzięki!
źródło
filter(Objects::nonNull)
zObjects
odjava.utils
Odpowiedzi:
Obecne myślenie wydaje się polegać na „tolerowaniu” wartości zerowych, to znaczy ogólnie na dopuszczaniu ich, chociaż niektóre operacje są mniej tolerancyjne i mogą w końcu spowodować wyrzucenie NPE. Zobacz dyskusję nulls na liście mailingowej grupy ekspertów Lambda Libraries, a konkretnie tę wiadomość . Następnie wyłonił się konsensus co do opcji nr 3 (z wyraźnym sprzeciwem ze strony Douga Lei). A więc tak, obawy PO dotyczące wybuchu rurociągów z NPE są uzasadnione.
Nie bez powodu Tony Hoare nazywał wartości zerowe „błędem za miliard dolarów”. Radzenie sobie z wartościami zerowymi to prawdziwy ból. Nawet w przypadku klasycznych kolekcji (bez uwzględnienia lambd lub strumieni) wartości null są problematyczne. Jak wspomniał fge w komentarzu, niektóre kolekcje dopuszczają wartości null, a inne nie. W przypadku kolekcji, które zezwalają na wartości null, wprowadza to niejednoznaczności do interfejsu API. Na przykład w przypadku Map.get () zwrot null wskazuje, że klucz jest obecny i jego wartość wynosi null, lub że klucz jest nieobecny. Trzeba wykonać dodatkową pracę, aby ujednoznacznić te przypadki.
Typowym zastosowaniem wartości null jest oznaczenie braku wartości. Podejście do radzenia sobie z tym zaproponowane dla Java SE 8 polega na wprowadzeniu nowego
java.util.Optional
typu, który hermetyzuje obecność / brak wartości, wraz z zachowaniami polegającymi na dostarczaniu wartości domyślnej lub rzucaniu wyjątku lub wywoływaniu funkcji itp. wartość jest nieobecna.Optional
jest używany tylko przez nowe API, jednak wszystko inne w systemie wciąż musi znosić możliwość zerowania.Radzę unikać rzeczywistych odniesień zerowych w największym możliwym stopniu. Z podanego przykładu trudno wywnioskować, jak może istnieć „zerowa” Wydra. Ale gdyby było to konieczne, sugestie OP dotyczące odfiltrowywania wartości zerowych lub mapowania ich na obiekt wartowniczy ( wzorzec obiektu zerowego ) są dobrym podejściem.
źródło
null
jest do bani, właśnie dlatego. Acc. do ankiety, którą widziałem (nie mogę jej znaleźć), NPE to wyjątek numer 1 w Javie. SQL nie jest językiem programowania wysokiego poziomu, a na pewno nie jest funkcjonalny, który gardzi wartością zerową, więc go to nie obchodzi.Chociaż odpowiedzi są w 100% poprawne, mała sugestia, aby poprawić
null
obsługę przypadku samej listy za pomocą opcji Opcjonalne :Ta część
Optional.ofNullable(listOfStuff).orElseGet(Collections::emptyList)
pozwoli ci ładnie obsłużyć przypadek, gdylistOfStuff
jest null i zwrócić emptyList zamiast niepowodzenia z NullPointerException.źródło
Odpowiedź Stuarta stanowi świetne wyjaśnienie, ale chciałbym podać inny przykład.
Napotkałem ten problem podczas próby wykonania a
reduce
na strumieniu zawierającym wartości null (w rzeczywistości tak byłoLongStream.average()
, co jest rodzajem redukcji). Ponieważ średnia () zwracaOptionalDouble
, założyłem, że Stream może zawierać wartości null, ale zamiast tego został zgłoszony wyjątek NullPointerException. Wynika to z wyjaśnienia Stuarta, że null v. Empty.Tak więc, jak sugeruje OP, dodałem taki filtr:
list.stream() .filter(o -> o != null) .reduce(..);
Lub, jak wskazano poniżej, użyj predykatu dostarczonego przez Java API:
Z dyskusji na liście mailingowej podlinkował Stuart: Brian Goetz o null w strumieniach
źródło
Jeśli chcesz tylko odfiltrować wartości null ze strumienia, możesz po prostu użyć odwołania do metody do java.util.Objects.nonNull (Object) . Z jego dokumentacji:
Na przykład:
List<String> list = Arrays.asList( null, "Foo", null, "Bar", null, null); list.stream() .filter( Objects::nonNull ) // <-- Filter out null values .forEach( System.out::println );
Spowoduje to wydrukowanie:
źródło
Przykład unikania null, np. Użyj filtra przed groupingBy
Odfiltruj wystąpienia null przed groupingBy.
Oto przykładMyObjectlist.stream() .filter(p -> p.getSomeInstance() != null) .collect(Collectors.groupingBy(MyObject::getSomeInstance));
źródło