Больше никаких NullPointer, начните использовать Optional в своем кодировании!


Введение

Всякий раз, когда я провожу собеседование с новым кандидатом, я спрашиваю его об использовании или знании Optional, и почти всегда слышу один и тот же ответ: «Я использовал его, когда использовал JPA»😢.

📦 Если вы хотите следить за полным классом во время чтения статьи, откройте его на GitHub — Optional-Stream-usecase-example

Дополнительно

Использование Optional выходит далеко за рамки JPA, он появился как решение для упрощения валидации нулевых объектов. В этой статье я покажу вам все способы его использования в повседневной работе и тем самым уменьшу на 99,9% вероятность получения NullPointer и сделаю ваш код чище.

isPresent

Когда мы манипулируем Optional, это дает нам функцию .isPresent(), которая отвечает за то, чтобы сказать нам, есть ли значение или оно равно null, возвращает булево false, а существующее значение будет true

 private boolean methodExampleReceivesOptional(Optional<String> opt) {
        return opt.isPresent();
 }
Войдите в полноэкранный режим Выход из полноэкранного режима

пустой

Если у вас есть метод, который может вернуть null, вместо возврата null возвращайте .empty()

private void returnEmptyOptional() {
    Optional<String> opt = Optional.empty();
    if (methodExampleReceivesOptional(opt))
        msg(VALUE_FOUND);
    else
        msg(VALUE_NOT_FOUND);
}

// print: "value not found"
Войдите в полноэкранный режим Выход из полноэкранного режима

из

Когда вы уверены, что возвращаемое значение не будет равно null, или когда вы собираетесь передать значение методу/функции, принимающей Optional, вы должны создать его с помощью .of(Object)

private void returnOptionalFixValue() {
    var opt = Optional.of("Sassine");
    if (methodExampleReceivesOptional(opt))
        msg(VALUE_FOUND);
    else
        msg(VALUE_NOT_FOUND);
}

// print: "value found"
Войдите в полноэкранный режим Выход из полноэкранного режима

Если мы попытаемся передать значение непосредственно в

methodExampleReceivesOptional("Sassine");
Войдите в полноэкранный режим Выход из полноэкранного режима

метод не будет работать, потому что он не может принимать значение, которое не является Optional

ofNullable

В этом случае мы не уверены, будет ли значение существовать или будет null, это будет зависеть от сгенерированного значения, поэтому мы используем .ofNullable(Object) Этот парень поможет вам избежать и обработать все ваши NullPointers сейчас!

private void returnDynamicOptional() {
    Random r = new Random();
    var n = r.nextInt((2 - 1) + 1) + 1;
    var opt = Optional.ofNullable(n == 1 ? null : "Sassine");
    if (methodExampleReceivesOptional(opt))
        msg(VALUE_FOUND);
    else
        msg(VALUE_NOT_FOUND);
}

// print: "value found" or "value not found"
Войдите в полноэкранный режим Выход из полноэкранного режима

получить

Когда у нас есть подтверждение того, что значение существует с помощью «ifPresent», мы можем восстановить значение, содержащееся в Optional, и манипулировать им так, как нам нужно, используя вызов .get()

private void getOptionalValue() {
    Optional<String> valueOpt = Optional.of("Sassine");
    if (methodExampleReceivesOptional(valueOpt))
        msg(valueOpt.get());
    else
        msg(VALUE_NOT_FOUND);
}

// print: Sassine
Войдите в полноэкранный режим Выход из полноэкранного режима

orElse

С помощью Optional OfNullable, если его значение равно null, мы можем сказать ему, чтобы он дал нам фиксированное значение по умолчанию или даже вызвал другую функцию для получения другого значения с помощью .orElse(Object) или .orElseGet(() -> args()).

    private void returnDefaultValueWhenNull() {
        String ex = null;
        String valueOpt = Optional.ofNullable(ex).orElse("Sassine - Default");
        msg("value found > %s".formatted(valueOpt));
        String valueOpt2 = Optional.ofNullable(ex).orElse(getName());
        msg("value found > %s".formatted(valueOpt2));
        String valueOpt3 = Optional.ofNullable(ex).orElseGet(Person::getName);
        msg("value found > %s".formatted(valueOpt3));
    }

/* 
print1: value found > Sassine - Default
print2: value found > Sassine - Function
print3: value found > Sassine - Class
*/

Войдите в полноэкранный режим Выход из полноэкранного режима

orElseThrow

Мы можем заставить Optional бросать исключение, как мы его определяем, когда значение равно null, используя .orElseThrow(() -> new Exception()).

private void returnCustomExecutionIfNull() {
    try {
        String ex = null;
        Optional.ofNullable(ex).orElseThrow(() -> new RuntimeException("value not found - exception"));
    } catch (RuntimeException e) {
        msg(e.getMessage());
    }
}

//print: value not found - exception
Войдите в полноэкранный режим Выход из полноэкранного режима

Дополнительно Java 9+

ifPresentOrElse

Чтобы упростить ifPresent и get, в java 9 был доступен .ifPresentOrElse((),()), что упрощает реализацию и принятие решений.

private void ifPresentOrElse() {
    String ex = null;
    Optional.ofNullable(ex)
    .ifPresentOrElse(v -> msg(VALUE_FOUND_S.formatted(v)), () -> msg(VALUE_NOT_FOUND));

    Optional.ofNullable("Sassine")
    .ifPresentOrElse(v -> msg(VALUE_FOUND_S.formatted(v)), () -> msg(VALUE_NOT_FOUND));
}

//print: value not found
//print2:  value found > Sassine
Войдите в полноэкранный режим Выход из полноэкранного режима

getValueOr

С помощью .or((() -> …), если значение 1 отсутствует, выполняется действие …. в данном примере получить значение, существующее в функции getName

private void getValueOr() {
    String ex = null;
    Optional.ofNullable(ex)
    .or(() -> Optional.of(getName()))
    .ifPresent(this::msg);
}

//print: Sassine - Function
Войдите в полноэкранный режим Выход из полноэкранного режима

optionalStream

Optional также позволяет использовать .stream(), если значение присутствует, то возвращается поток, иначе возвращается пустой поток!

private void optionalStream() {
    var list = List.of("AB", "CD");
    Optional.ofNullable(list).stream().forEach(l -> {
        l.forEach(this::msg);
    });
    List<String> x = null;
    Optional.ofNullable(x).stream().forEach(l -> {
        l.forEach(this::msg);
    });
}

//print: ABCD
//print2: 
Войдите в полноэкранный режим Выход из полноэкранного режима

Если вы хотите взглянуть/протестировать, проект находится на моем github 😉

Оцените статью
Procodings.ru
Добавить комментарий