Введение
Всякий раз, когда я провожу собеседование с новым кандидатом, я спрашиваю его об использовании или знании 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 😉