Объединение всех подмножеств в потоке в Java optionals

#java #optional

#java #тип параметра

Вопрос:

Это исходный фрагмент кода:

 Set<StatuteType> statuteTypes = registration.getStudent().getStudentStatutesSet()
    .stream()
    .map(StudentStatute_Base::getType)
    .collect(Collectors.toSet());
  

Я хочу обернуть все в необязательный, чтобы избежать нулевых указателей и всего остального. Если студент не существует или statutesSet не существует.

Что у меня есть:

 Set<StatuteType> statuteTypes = Optional.of(registration)
            .map(Registration_Base::getStudent)
            .map(student -> student.getStudentStatutesSet())
            .flatMap(Collection::stream)
            .map(StudentStatute_Base::getType)
            .collect(Collectors.toSet())
            .orElse(null);
  

Возможно ли что-то подобное? Я хочу избежать нулевых проверок в этой цепочке, и если есть какой-либо null, просто верните простой null, а вместо этого получите исключение.

Обычно, я думаю, было бы логично использовать flatMap, как описано здесь, но в данном случае это не кажется правильным, потому что необязательный flatmap возвращает необязательный.

Комментарии:

1.Итак registration getStudent() , getStudentStatutesSet() и getType все могут вернуться null , и вы хотите их обработать?

2. Optional не предназначен для использования таким образом. Не предполагается заменять проверки null в цепочке методов, как вы пытаетесь выполнить. Вместо этого используйте его в качестве возвращаемого типа, если у вас есть возможность изменить свои Registration Student классы and . Если вас интересует этот подход, я мог бы написать ответ

3. .orElse(null) создает нулевую проблему, от которой, по вашим словам, вы хотели избавиться. Вместо этого используйте .orElse(Collections.emptySet()) .

4. Если вы хотите игнорировать возможные значения null, просто отфильтруйте поток: .filter(Objects::nonNull)

5. @fps Я никому не доверяю. Я согласен с тем, что показано на экране в том месте видео, на которое вы ссылаетесь, но это когда у вас есть только один уровень. Представьте себе 5 уровней. a.getB().getC().getD().getE() . Какую альтернативу Optional#map цепочке вы бы предложили? 5 уровней if-null-проверок? Я все еще думаю, что это допустимое использование Optional . Как документация API, так и слова Стюарта Маркса Optional , в первую очередь предназначены для типа возвращаемого метода. Это не исключает такого рода использования.

Ответ №1:

Вот простой способ сделать это:

 Set<StatuteType> statuteTypes = Optional.ofNullable(registration)
    .map(Registration_Base::getStudent)
    .map(student -> student.getStudentStatutesSet())
    .map(Collection::stream)
    .orElseGet(Stream::empty)    // Exit Optional, enter stream
    .map(StudentStatute_Base::getType)
    .collect(Collectors.toSet());
  

Однако это не приводит к набору null. Коллекции никогда не должны быть нулевыми, только пустыми. Я бы рекомендовал этот подход. Весь смысл использования Optional объекта в том, чтобы вам никогда не приходилось иметь дело с нулевыми значениями.

Комментарии:

1. Идеальный. Но это должно быть .OrElse(Stream.empty()) иначе это не сработает.

2. Моя ошибка. Однако вы можете использовать .orElseGet(Stream::empty) . Это не приведет к инициализации потока, если Optional значение не равно null.

Ответ №2:

Collection::stream не возвращает an Optional , поэтому вы не должны использовать flatMap здесь. Вы должны продолжать использовать map необязательный.

.map(Collection::stream) дает вам Optional<Stream<Statute>> . Похоже, вы пытаетесь вызвать методы потока map и collect для этого. Но вам нужно сначала вызвать Optional.map , прежде чем вы сможете это сделать.

Вы также должны использовать Optional.ofNullable if registration может быть null:

 Set<StatuteType> statuteTypes = Optional.ofNullable(registration)
    .map(Registration_Base::getStudent)
    .map(student -> student.getStudentStatutesSet())
    .map(Collection::stream)
    .map(x -> // Optional.map
        x.map(StudentStatute_Base::getType) // Stream.map
            .filter(Objects::nonNull) // I assume you want to filter out the statute types which are null?
            .collect(Collectors.toSet())
    )
    .orElse(null);