Introdução
Sempre que estou entrevistando um novo candidato eu o questiono sobre o uso ou conhecimento do Optional, e quase sempre ouço a mesma resposta "Já utilizei, quando usei o JPA "😢
📦 Caso queira acompanhar a classe completa enquanto lê o artigo acesse ele no GitHub - Optional-Stream-usecase-example
Optional
O uso do Optional vai muito além do JPA ele veio como uma solução para ser utilizado para simplificar as validações de objetos nulos, neste post vou mostrar a vocês todas as formas de utilizar ele no seu dia dia e com isso reduzir em 99.9% as chances de se obter um NullPointer e tornar o seu código mais limpo.
isPresent
Quando manipulamos Optional ele nos entrega uma função que é os .isPresent() que é responsável por nos informar se existe um valor ou se é nulo retorna um booleano falso e existir valor será verdadeiro
private boolean methodExampleReceivesOptional(Optional<String> opt) {
return opt.isPresent();
}
empty
Sempre que tiver um método que o retorno poderá ser nulo invés de retornar null retorne .empty()
private void returnEmptyOptional() {
Optional<String> opt = Optional.empty();
if (methodExampleReceivesOptional(opt))
msg(VALUE_FOUND);
else
msg(VALUE_NOT_FOUND);
}
// print: "value not found"
of
Quando tiver certeza que o valor a ser retornado não será nulo, ou quando você for enviar um valor para um método/função que recebe Optional, você devera criar ele utilizando .of(Object)
private void returnOptionalFixValue() {
var opt = Optional.of("Sassine");
if (methodExampleReceivesOptional(opt))
msg(VALUE_FOUND);
else
msg(VALUE_NOT_FOUND);
}
// print: "value found"
Se tentarmos passar um valor direto para o método
methodExampleReceivesOptional("Sassine");
não funcionará pois ele não pode receber um valor que não seja Optional
ofNullable
Neste caso não temos certeza se o valor irá existir ou será nulo, vai depender do valor gerado então utilizamos o .ofNullable(Object) esse cara é quem irá te acompanhar para evitar e tratar todos seus NullPointers agora!
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"
get
Quando temos a confirmação de que o valor existe com o "ifPresent" podemos recuperar o valor contido no Optional e manipularmos da forma que quisermos utilizando a chamada .get()
private void getOptionalValue() {
Optional<String> valueOpt = Optional.of("Sassine");
if (methodExampleReceivesOptional(valueOpt))
msg(valueOpt.get());
else
msg(VALUE_NOT_FOUND);
}
// print: Sassine
orElse
Com Optional OfNullable se o seu valor for nulo conseguimos falar para ele nos entregar um valor padrão fixado ou até mesmo chamar outra função para recuperar outro valor utilizando .orElse(Object) ou .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
Conseguimos fazer com que o Optional lance uma Exception conforme a gente definir quando o valor for nulo utilizando .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
Optional Java 9+
ifPresentOrElse
Para simplificar os ifPresent e o get, no java 9 foi disponibilizado o .ifPresentOrElse((),()) que simplifica a implementação e a tomada de decisão
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
Com .or(() -> ...) se o 1 valor não estiver presente ele executa uma ação ... neste exemplo buscar o valor existente na função getName
private void getValueOr() {
String ex = null;
Optional.ofNullable(ex)
.or(() -> Optional.of(getName()))
.ifPresent(this::msg);
}
//print: Sassine - Function
optionalStream
Optional também permite o uso de .stream(), se um valor estiver presente ele retorna o stream caso contrario retorna uma stream vazia!
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:
Se quiser dar uma olhada/testar o projeto está em meu github ;)