Почему это бросает 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
IS null
, который является вполне приемлемым значением. Более того, findFirst()
возвращает Optional , что имеет еще больше смысла для findFirst()
обработки null
s.
РЕДАКТИРОВАТЬ: обновлено, orElse()
чтобы быть менее двусмысленным.
java
java-8
java-stream
optional
бесконечный
источник
источник
String
, что, если это список, представляющий столбец в БД? Значение первой строки для этого столбца может бытьnull
.null
вообще говоря, вполне приемлемое значение для Java. В частности, это допустимый элемент дляArrayList<String>
. Однако, как и с любым другим значением, есть ограничения на то, что с ним можно сделать. «Никогда не употребляйтеnull
» - бесполезный совет, поскольку вы не можете его избежать.findFirst()
, вам больше нечего делать.Ответы:
Причина этого - использование
Optional<T>
в возврате. Необязательно содержатьnull
. По сути, он не предлагает возможности различать ситуации «это не там» и «это есть, но он установленnull
».Вот почему в документации явно запрещена ситуация, когда
null
выбрано вfindFirst()
:источник
Optional
. В любом случае, я думаю, что я граничу с разглагольствованием - если язык его не поддерживает, он не поддерживает его.boolean
для различения этих двух ситуаций имело бы смысл. На мой взгляд, использованиеOptional<T>
здесь было сомнительным выбором.Iterable
типа, проверяетhasNext()
и возвращает соответствующее значение.findFirst
возвращает пустое факультативное значение в случае , если ЗПКак уже обсуждалось , разработчики API не предполагают, что разработчик хочет обрабатывать
null
значения и отсутствующие значения одинаково.Если вы все еще хотите это сделать, вы можете сделать это явно, применив последовательность
к ручью. Результатом будет пустой необязательный параметр в обоих случаях, если нет первого элемента или если первый элемент есть
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"
когда элемент не существует.источник
null
либо для 1) первого элемента,null
либо 2) в списке нет элементов. Я обновил вопрос, чтобы устранить двусмысленность.Optional
может быть назначеноnull
. ПосколькуOptional
предполагается, что это «тип значения», он никогда не должен быть нулевым. И не обязательно сравнивать==
. Код может дать сбой в Java 10 :) или когда в Java вводится тип значения.null
и хотя экземпляры никогда не должны сравниваться==
, ссылка может быть проверена дляnull
использования,==
поскольку это единственный способ проверить ееnull
. Я не понимаю, как такой переход к «никогдаnull
» должен работать для существующего кода, поскольку даже значение по умолчанию для всех переменных экземпляра и элементов массиваnull
. Фрагмент, безусловно, не лучший код, но и задача обработкиnull
s как текущих значений - тоже.null
. Однако, поскольку такое гипотетическое изменение языка приведет к тому, что компилятор выдаст здесь ошибку (не нарушит код молча), я могу смириться с тем фактом, что его, возможно, придется адаптировать для Java 10. Я полагаю,Stream
API будет выглядят совсем по-другому…Вы можете использовать
java.util.Objects.nonNull
для фильтрации списка перед поискомчто-то типа
источник
firstString
быть,null
если первый предметstrings
естьnull
.Optional.of
что не является нулевым. Вы моглиmap
бы,Optional.ofNullable
а затем использовать,findFirst
но в конечном итоге у вас будет Необязательный или НеобязательныйСледующий код заменяется
findFirst()
наlimit(1)
и заменяетсяorElse()
наreduce()
:String firstString = strings. stream(). limit(1). reduce("StringWhenListIsEmpty", (first, second) -> second);
limit()
позволяет достичь только 1 элементаreduce
.BinaryOperator
Передаютсяreduce
возвраты , что один элемент или же ,"StringWhenListIsEmpty"
если ни один из элементов не достигнутыreduce
.Прелесть этого решения в том, что
Optional
он не выделяется, иBinaryOperator
лямбда ничего не выделяет.источник
Необязательный должен быть типом «значение». (прочтите мелкий шрифт в javadoc :) JVM может даже заменить все
Optional<Foo>
на простоFoo
, удалив все затраты на упаковку и распаковку.null
Foo означает пустойOptional<Foo>
.Возможен вариант, позволяющий разрешить Optional с нулевым значением без добавления логического флага - просто добавьте объект-дозорный. (может даже использовать
this
качестве дозорного; см. Throwable.cause)Решение о том, что Optional не может обернуть null, не основано на стоимости выполнения. Это была очень обсуждаемая проблема, и вам нужно покопаться в списках рассылки. Решение не для всех убедительно.
В любом случае, поскольку Optional не может обернуть нулевое значение, он загоняет нас в угол в таких случаях, как
findFirst
. Они, должно быть, рассудили, что нулевые значения очень редки (даже считалось, что Stream должен блокировать нулевые значения), поэтому удобнее генерировать исключение для нулевых значений, а не для пустых потоков.Обходной путь - бокс
null
, напримерclass Box<T> static Box<T> of(T value){ .. } Optional<Box<String>> first = stream.map(Box::of).findFirst();
(Говорят, решение каждой проблемы ООП - ввести другой тип :)
источник
Box
тип создавать не нужно . СамOptional
тип может служить этой цели. См. Мой ответ для примера.null
это допустимое значение, как и любое другое, без особой обработки. (пока не поздно :)