Как положить в Optional<> null по результатам работы stream

Предположим, есть сущность Box

public class Box {
public int id;
public String name;

public Box(int id, String name) {
    this.id = id;
    this.name = name;
}

public Box(int id) {
    this.id = id;
}

Мне приходит список этих коробок, и я должен узнать имя конкретной коробки (оно может быть null) Как я это делаю:

Box box1 = new Box(1, "1");
Box box2 = new Box(2);
List<Box> list = Arrays.asList(box1, box2);
Optional<String> boxName = list.stream()
        .filter(b -> b.id == 2)
        .map(b -> b.name).findAny(); //<-тута вот упадет

String name = boxName.orElse(null);

Получаю такую вот ошибку:

Exception in thread "main" java.lang.NullPointerException
    at java.base/java.util.Objects.requireNonNull(Objects.java:221)
    at java.base/java.util.Optional.<init>(Optional.java:107)
    at java.base/java.util.Optional.of(Optional.java:120)
    at java.base/java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:194)
    at java.base/java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:191)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:548)
    at Main.main(Main.java:39)

Я знаю что можно решить задачку так:

Box box1 = new Box(1, "1");
Box box2 = new Box(2);
List<Box> list = Arrays.asList(box1, box2);
Box box = list.stream()
        .filter(b -> b.id == 2).findAny().orElse(null);

String name = box != null ? box.name : null;

Но мне кажется, что просто чего-то не хватает в первом варианте.


Ответы (2 шт):

Автор решения: Roman-Stop RU aggression in UA

В Optional в принципе нельзя положить значение null. Можно только создать Optional.empty, т.е. отсутствие значения.

То, что вам нужно делается так:

Optional<String> boxName = list.stream()
          .filter(b -> b.id == 2 && b.name != null)
          .map(b -> b.name)
          .findAny()

или так:

Optional<String> boxName = list.stream()
          .filter(b -> b.id == 2)
          .map(b -> b.name)
          .filter(name -> name != null)
          .findAny()
→ Ссылка
Автор решения: Nowhere Man

Почему бы не написать проще:

  • найти бокс по идентификатору при помощи Stream::findAny / Stream::findFirst, получив Optional<Box>
  • преобразовать Box -> String при помощи Optional::map, получив Optional<String>, при этом name может быть null для некоторого бокса.
  • для случая, когда бокс не найден, вернуть null для имени
String boxName = list.stream() // Stream<Box>
                     .filter(b -> b.id == 2) // Stream<Box>
                     .findAny() // Optional<Box>
                     .map(b -> b.name) // Optional<String> - уже берется name
                     .orElse(null); // если не найден бокс, ТОЖЕ будет null
→ Ссылка