Технологии

Обновлённый удобный Optional в Java 9

732
Обновлённый удобный Optional в Java 9

Сергей Хоменко

ведущий программист департамента аналитических систем, R-Style Softlab

Класс java.util.Optional появился в языке, начиная с Java 8. В Java 9 он получил обновлённый API. Предлагаем ознакомиться с различными возможностями этого класса на конкретных примерах, в том числе связанных с обработкой NullPointerException (NPE).

Любой Java-программист знает, что едва ли не самой частой ошибкой в программах на этом языке является попытка вызвать метод по нулевой ссылке на объект (NPE). Также разработчики часто сталкиваются с необходимостью получения данных из разных источников с учетом их приоритета. Для решения и той, и другой задачи отлично подходит класс java.util.Optional. Вот о нем и пойдет речь в нашей статье.

Проверка на null с Optional и без

Чтобы лучше понять, насколько упростилась жизнь java-программиста с появлением Optional, рассмотрим на конкретном примере, как выполнялась проверка на null до выхода Java 8 и сейчас. Представьте, что у вас есть такие классы:

public class BAZ {
private String code;
public BAZ(Integer suffix) {
this.code = “BAZ_” + suffix;
    }
    @Override
public String toString() {…}
public String getCode() {returncode;}
 }
public class BAR {
private String code;
private BAZ baz;
public BAR(String code, Integer id) {
this.code = code;
if (id > 50) {
this.baz = new BAZ(id * 2);
         }
     }
     @Override
public String toString() {…}
public String getCode() {returncode;}
public BAZ getBaz() {returnbaz;}
 }
public class FOO {
private static final Random RANDOM = new Random();
private String code;
private List<BAR> bars;
public FOO(String code) {
this.code = code;
this.bars = RANDOM.ints(RANDOM.nextInt(5), 0, 100)            
                 .mapToObj(i -> i % 2 > 0 ? new BAR(“CODE_” + i, i) : null)
                 .collect(Collectors.toList());
 }
     @Override
public String toString() {…}
public String getCode() {returncode;}
public List getBars() {returnbars;}
 }

Предположим, вы хотите получить code из baz у каждого BAR в списке bars у любого FOO. Чтобы все прошло гладко, придется использовать пресловутую проверку на null:

@Test
public void oldStyle() {
for (FOO foo : foos) {
        List bars = foo.getBars();
if (bars == null) {
            System.out.println(“FOO ‘bars’ is null => nothing to do”);
        } else {
for (BAR bar : bars) {
if (bar == null) {
                    System.out.println(“BAR is null => no BUZ indeed”);
                } else {
                    BAZ baz = bar.getBaz();
if (baz == null) {
                        System.out.println(“BAR is not null, but BUZ is”);
                    } else {
                        System.out.println(baz.getCode());
                    }
                }
            }
        }
    }
}

Как вы наверняка знаете, в среднестатистическом проекте таких проверок может быть сотни и даже больше. Недаром в Intellij IDEA есть встроенный шаблон ifn для генерации проверки на null (рис. 1).

ris1.jpg

Рис. 1. Шаблон ifn для генерации проверки на null в Intellij IDEA

А вот так вы сможете оптимизировать этот код с помощью класса Optional, реализованного в Java 8:

@Test
public void shinyNew8Style() {
foosWithNotOptionalsFields.stream()
        .map(FOO::getBars).map(Optional::ofNullable)
        .flatMap(bars -> bars.map(Stream::of).orElseGet(Stream::empty))                   
        .flatMap(Collection::stream)
        .map(Optional::ofNullable).map(bar -> bar.map(BAR::getBaz))
        .map(baz -> baz.map(BAZ::getCode))
        .forEach(code -> code.ifPresent(System.out::println));
 }

Возьмем еще один пример. Допустим, приложение использует два сервиса, основной One и запасной Two. Вам нужно получить сущность, и, если по какой-то причине это не удалось (сбой связи, ошибка БД и др.), выдать ошибку для ее дальнейшей обработки.

Код с использованием Optional будет выглядеть следующим образом:

@Test
public void optionalEntity8Style() { BAR bar = BAR.getBARFromServiceOne(ID) .orElse(BAR.getBARFromServiceTwo(ID).orElseThrow(ERROR_SUPPLIER)); }

Optional в Java 9: новые методы API

В Java 9 в класс Optional добавлено три новых метода: stream(), ifPresentsOrElse() и or(). Рассмотрим их более подробно.

Optional::stream. Данный метод, как бы странно это не звучало, позволяет преобразовывать Optional в Stream. Если Optional::isPresent, то полученный Stream будет содержать Optional::get, в противном случае Stream будет пустым. Упростим пример с проверкой на null:

@Test
public void shinyNew9Style() {
foosWithNotOptionalsFields.stream()
            .flatMap(foo -> Optional.ofNullable(foo.getBars()).stream())
           .flatMap(Collection::stream).map(Optional::ofNullable)
           .flatMap(Optional::stream)
           .flatMap(bar -> Optional.ofNullable(bar.getBaz()).stream())
           .forEach(baz -> System.out.println(baz.getCode())); }

Здесь каждый Stream, полученный из Optional, уже содержит отфильтрованные элементы, и необходимость в проверке на null отпадает вовсе! Вызов bar.getBaz() в этих строчках абсолютно безопасен, даже если baz и был .empty, преобразование к Stream позаботится о том, чтобы в потоке не было значений null.

Optional::or. Позволяет легко создавать цепочки вызовов методов, так как возвращает Optional. Для примера воспользуюсь методом получения сущностей из внешних сервисов, только добавлю еще парочку для наглядности (ради интереса попробуйте реализовать такую «цепочку» на Java 8):

@Test
public void optionalEntity9Style() {
    BAR bar = BAR.getBARFromServiceOne(ID)
            .or(() -> BAR.getBARFromServiceTwo(ID))
            .or(() -> BAR.getBARFromServiceThree(ID))
            .or(() -> BAR.getBARFromServiceFour(ID))
            .orElseThrow(ERROR_SUPPLIER); }

Optional::ifPresentOrElse. Метод позволяет определить последовательность действий в зависимости от того, присутствует ли заданное значение в Optional.

@Test
public void optionalEntityIfPresentOrElse() {
    BAR.getBARFromServiceOne(ID)
            .or(() -> BAR.getBARFromServiceTwo(ID))
            .or(() -> BAR.getBARFromServiceThree(ID))
            .or(() -> BAR.getBARFromServiceFour(ID))
            .ifPresentOrElse(System.out::println,
                       () -> System.out.println(“Не удалось создать BAR”));
}

Начиная с Java 10 разработчики заметят еще один новый метод класса Optional — orElseThrow(). По сути, он делает то же самое, что и get(), — возвращает значение из Optional, если таковое существует либо генерирует исключение NoSuchElementException. Тем не менее, авторы языка рекомендуют использовать именно orElseThrow() вместо метода get(), который, по их мнению, является опасным благодаря своей сигнатуре и нивелирует все преимущества применения класса.

Использование Optional в коде при работе с объектами, наличие которых определяется во время исполнения, гораздо удобнее проверок с использованием if()…else(). В то же время применять Optional в качестве типа поля класса — плохая идея, не для того этот класс создавали. Как заметил Brian Goetz (Senior Java Language Architect в Oracle, работает над развитием языка и платформы Java), основным намерением при добавлении этой возможности в язык было предоставить механизм для описания возвращаемых значений библиотечных методов в тех случаях, когда нужен способ показать состояние «отсутствия результата», не прибегая к null.

Похожие записи

Ключевые продукты R-Style Softlab на FINOPOLIS2024

Новости компании Обновления продуктов Технологии Тренды

16-18 октября на территории Университета «Сириус» в Сочи пройдет форум инновационных финансовых технологий FINOPOLIS 2024. Делегация R-Style Softlab примет участие в составе группы Россельхозбанка, а на стенде компании в секторе «Финансовый рынок», место Р-34 будет организован лекторий. Расскажем о подходе «Банк под ключ» и докладах, расписании выступлений для посетителей форума можно посмотреть на нашем сайте.  […]

647

R-Style Softlab приняла участие в крупнейшей выставке АПК «Золотая осень – 2024»

Новости компании Технологии

Компания R-Style Softlab, входящая в группу Россельхозбанка приняла участие в крупнейшей выставке АПК «Золотая осень – 2024» в Москве. 26-я Российская агропромышленная выставка «Золотая осень» — одно из самых значимых и масштабных событий в области сельского хозяйства. Это ведущая площадка для демонстрации достижений агропромышленного комплекса и обсуждения ключевых вопросов развития отрасли. Стратегическим партнером выставки выступает […]

649

R-Style Softlab заключила партнерское соглашение с разработчиком AI-решений для бизнеса — компанией Dbrain

Новости компании Технологии

R-Style Softlab, компания-разработчик ПО для финансовой сферы и системный интегратор, входящий в группу Россельхозбанка заключила партнерское соглашение c российской компанией Dbrain (ООО «Дибрейн»), специализирующейся на AI-решениях для организации документооборота в банках и финансовых компаниях. Dbrain – компания-разработчик цифровых продуктов для структурирования массива документов, верификации подлинности клиентских сведений, а также распознавания данных с помощью нейросетей. Так, […]

1096