Почему findFirst () бросает исключение NullPointerException, если первый элемент, который он находит, равен null?
почему это бросает 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
первый элемент strings и null, что является вполне приемлемым значением. Кроме того, findFirst() возвращает дополнительно, что имеет еще больше смысла для findFirst() чтобы иметь возможность обрабатывать null s.
изменить: обновлено orElse() чтобы быть менее двусмысленным.
5 ответов:
причиной этого является использование
Optional<T>В возврат. Необязательно не допускается содержатьnull. По сути, он не предлагает никакого способа различения ситуаций "это не там "и" это там, но он установлен вnull".вот почему документация явно запрещает ситуацию, когда
nullустановлен вfindFirst():Броски:
NullPointerException- если элемент выбранnull
как уже обсудили, разработчики API не предполагают, что разработчик хочет лечить
nullзначения и отсутствующие значения таким же образом.если вы все еще хотите это сделать, вы можете сделать это явно, применив последовательность
.map(Optional::ofNullable).findFirst().flatMap(Function.identity())в поток. Результат будет пустым необязательным в обоих случаях, если нет первого элемента или если первый элемент
null. Так что в вашем случае, вы можете использоватьString firstString = strings.stream() .map(Optional::ofNullable).findFirst().flatMap(Function.identity()) .orElse(null);получить
nullзначение, если первый элемент отсутствует либоnull.если вы хотите различать эти случаи, вы можете просто опустить
flatMapдействие: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"));это не сильно отличается от вашего обновленного вопроса. Вы просто должны заменить
"no such element"С"StringWhenListIsEmpty"и"first element is null"Сnull. Но если вам не нравятся условные обозначения, вы можете достичь этого также, как:String firstString = strings.stream().skip(0) .map(Optional::ofNullable).findFirst() .orElseGet(()->Optional.of("StringWhenListIsEmpty")) .orElse(null);теперь
firstStringбудетnullесли элемент существует, ноnullи это будет"StringWhenListIsEmpty"когда элемент не существует.
следующий код заменяет
findFirst()Сlimit(1)и заменяетorElse()Сreduce():String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()позволяет только 1 элемент для достиженияreduce. ЭлементBinaryOperatorперешло кreduceвозвращает 1 элемент или другой"StringWhenListIsEmpty"если никакие элементы не достигаютreduce.красота этого решения заключается в том, что
Optionalне выделяется и тоBinaryOperatorлямбда не собирается ничего выделять.
можно использовать
java.util.Objects.nonNullчтобы отфильтровать список, прежде чем найтичто-то вроде
list.stream().filter(Objects::nonNull).findFirst();
необязательным должен быть тип" значение". (читайте мелкий шрифт в документация:) JVM может даже заменить все
Optional<Foo>СFoo, удаление всех затрат на бокс и распаковку. АnullFoo означает пустойOptional<Foo>.это возможный дизайн, чтобы разрешить необязательный с нулевым значением, без добавления логического флага-просто добавьте объект sentinel. (можно даже использовать
thisв качестве дозорного; видеть подобное.причина)решение, которое необязательно не может обернуть null не основан на стоимости выполнения. Это был очень спорный вопрос, и вам нужно копать списки рассылки. Решение не является убедительным для всех.
в любом случае, поскольку Optional не может обернуть нулевое значение, он толкает нас в угол в таких случаях, как
findFirst. Они должны были рассудить, что нулевые значения очень редки (даже считалось, что поток должен содержать нулевые значения), поэтому удобнее выбрасывать исключение на нулевые значения, а не на пустые потоки.A обходной путь заключается в поле
null, например,class Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();(они говорят, что решение каждой проблемы ООП состоит в том, чтобы ввести другой тип :)