Tenho certeza que em algum momento da sua vida como programador, já passou vergonha com um feio NullPointerException, ou estou mentindo? :)
Neste post vou te mostrar a nova classe Optional do Java 8, que irá te ajudar em dois pontos: evitar a famosa exceção NullPointerException e deixar seu código mais bonito e limpo.
Como sempre gosto de fazer, vou criar um exemplo que simula algo na vida real, que você pode encontrar no seu dia a dia como programador, mesmo sendo bem simples.
O sistema é um cadastro de motoristas de caminhões para uma empresa de logística. Cada motorista pode ter o seu caminhão próprio que pode ou não ter um seguro.
Repare que eu disse pode ter o seu caminhão próprio, ou seja, não é obrigatório ter um, o motorista pode alugar ou simplesmente ser terceirizado. O seguro também é opcional nesse caso.
Veja o diagrama de classes desse exemplo abaixo:

Vamos pensar uma tela de consulta desse sistema, o usuário informa o nome do motorista para recuperar a cobertura do caminhão. Isso seria importante por exemplo, para decidirmos se determinada carga será entregue pelo motorista A, B ou C.
Vamos implementar do modo pré Java 8 para conseguirmos enxergar as mudanças aos poucos. Segue abaixo a implementação das classes Motorista, Caminhao e Seguro:
public class Motorista {
private String nome;
private Integer idade;
private Caminhao caminhao;
// construtor, getters e setters
}
public class Caminhao {
private String modelo;
private Seguro seguro;
// construtor, getters e setters
}
public class Seguro {
private String cobertura;
private BigDecimal valorFranquia;
// construtor, getters e setters
}
Para não dificultar o exemplo, a consulta do motorista será feita em uma classe Motoristas, que faz a busca em um Map inicializado na construção do objeto. Vamos determinar que não existem dois motoristas com o mesmo nome.
public class Motoristas {
private Map<String, Motorista> motoristas = new HashMap<>();
public Motoristas() {
Seguro seguro = new Seguro("Parcial - não cobre roubo", new BigDecimal("5000"));
Caminhao caminhao = new Caminhao("Mercedes Atron", seguro);
Motorista motoristaJoao = new Motorista("João", 40, caminhao);
Motorista motoristaJose = new Motorista("José", 25, null); // Não tem caminhão
motoristas.put("João", motoristaJoao);
motoristas.put("José", motoristaJose);
}
public Motorista porNome(String nome) {
return motoristas.get(nome);
}
}
Agora, vamos ao código que poderíamos usar para imprimir a cobertura de um caminhão que pertence a um motorista:
Motorista motorista = motoristas.porNome("João");
String cobertura = motorista.getCaminhao().getSeguro().getCobertura();
cobertura = cobertura != null ? cobertura : "Sem seguro";
Tenho certeza que quando você leu esse código pensou: “Xiiii… isso pode dar um NullPointerException horrível na tela!
Então, vamos resolver:
Motorista motorista = motoristas.porNome("João");
String cobertura = "Sem seguro";
if (motorista != null) {
Caminhao caminhao = motorista.getCaminhao();
if (caminhao != null) {
Seguro seguro = caminhao.getSeguro();
if (seguro != null) {
cobertura = seguro.getCobertura();
}
}
}
Isso é o que chamamos de código verboso, ou seja, muita coisa escrita para fazer pouca coisa! Além de ser chato de ler.
Ao final do artigo, você irá aprender a reduzir esse código para:
String cobertura = motoristas.porNome("João")
.flatMap(Motorista::getCaminhao)
.flatMap(Caminhao::getSeguro)
.map(Seguro::getCobertura)
.orElse("Sem seguro");
Ficou animado? :) Então aguente firme, deixa eu começar te explicando alguns fundamentos do que é o Optional e, logo logo chegaremos a um código simples e bonito, que você pode usar para impressionar sua(seu) namorada(o).
Em primeiro lugar, é importante dizer que o Optional não veio para substituir o null no Java, mas sim ajudá-lo a pensar o que fazer quando o valor não está presente, consequentemente evitando o famoso e feio NullPointerException.
Você pode pensar no Optional como sendo uma caixa que pode ou não ter um valor dentro, se não tiver nada, dizemos que a caixa está vazia.
Para criar um objeto do tipo Optional, que fica no pacote java.util, podemos usar o método estático of, por exemplo:
Seguro seguro = new Seguro("Total com franquia reduzida", new BigDecimal("600"));
Optional seguroOpcional = Optional.of(seguro);
Repare que o Optional usa o generics para determinar o tipo de objeto que ele está guardando, no caso acima, Seguro.
Antes de aprendermos a mapear um Optional, deixa eu só te contar que, se você tentar passar null para o método of, será lançado uma NullPointerException. Então, se existir a possibilidade da criação de um Optional vazio, utilize o método ofNullable, veja abaixo:
Seguro seguro = null; Optional seguroOpcional = Optional.ofNullable(seguro);
Pense na seguinte situação: queremos imprimir na tela o valor da franquia, mas caso o seguro não exista, simplesmente não iremos fazer nada.
Repare que, com um código bem simples de entender conseguimos fazer isso, veja abaixo:
seguroOpcional.map(Seguro::getValorFranquia).ifPresent(System.out::println);
Deixa eu te contar como você pode ler esse código em português: no objeto seguroOpcional, pegue o valor da franquia e, caso exista, imprima na tela por favor.
Tecnicamente o que ocorre é: o método map retorna um novo Optional de BigDecimal, pois usamos o Seguro::getValorFranquia no mapeamento.
E o método ifPresent será executado se existir alguma coisa no Optional, neste caso ele chama o System.out::println passando o valor da franquia.
Então agora pense comigo, se o Seguro é opcional no Caminhao e este é opcional no Motorista, o que acha de alterarmos estes atributos em cada classe para um Optional? Veja como poderia ficar:
public class Caminhao {
private String modelo;
private Optional seguro;
public Caminhao(String modelo, Optional seguro) {
this.modelo = modelo;
this.seguro = seguro;
}
// getters e setters
}
public class Motorista {
private String nome;
private Integer idade;
private Optional caminhao;
public Motorista(String nome, Integer idade, Optional caminhao) {
this.nome = nome;
this.idade = idade;
this.caminhao = caminhao;
}
// getters e setters
}
A classe de consulta seria alterada para:
public class Motoristas {
private Map<String, Optional> motoristas = new HashMap<>();
public Motoristas() {
Seguro seguro = new Seguro("Parcial - não cobre roubo", new BigDecimal("5000"));
Caminhao caminhao = new Caminhao("Mercedes Atron", Optional.ofNullable(seguro));
Optional motoristaJoao = Optional.of(new Motorista("João", 40, Optional.ofNullable(caminhao)));
Optional motoristaJose = Optional.of(new Motorista("José", 25, Optional.ofNullable(null)));
motoristas.put("João", motoristaJoao);
motoristas.put("José", motoristaJose);
}
public Optional porNome(String nome) {
return motoristas.get(nome);
}
}
Já te falei que o método map retorna um outro Optional, certo? Então você poderia pensar, basta eu encadear várias chamadas desse método, mas… veja só, o código abaixo não compila:
Optional caminhaoOpcional = motoristas.porNome("João")
.map(Motorista::getCaminhao);
Sabe por quê? Porque como o atributo em Motorista é do tipo Optional<Caminhao>, ele irá retornar um Optional<Optional<Caminhao>>.
Pensando nisso que eles criaram o método flatMap, que podemos traduzir como “achate o Optional“, ou seja, ao invés de optional de optional de caminhão, será devolvido um optional de caminhão. Veja abaixo, o código agora está certo, compila sem problemas:
Optional caminhaoOpcional = motoristas.porNome("João")
.flatMap(Motorista::getCaminhao);
Os caras pensam em tudo mesmo, não é verdade!? hehe
Vamos encadear a chamada dos métodos até chegarmos ao último Optional e chamar orElse que já te explico o que faz.
String cobertura = motoristas.porNome("João")
.flatMap(Motorista::getCaminhao)
.flatMap(Caminhao::getSeguro)
.map(Seguro::getCobertura)
.orElse("Sem seguro");
Repare que usamos o map em map(Seguro::getCobertura), pois nesse caso, o atributo cobertura de Seguro já é uma String direta, e não um Optional.
Você está se perguntando, o que é esse orElse ai?
É outro método bem interessante do Optional. Leia da seguinte forma: se o Optional tiver algum resultado, retorne, caso contrário retorne esse valor aqui.
No caso do código acima, caso o motorista não tenha caminhão, ou não tenha seguro, será retornado a String “Sem seguro”.
Para onde foram aquelas quantidades de ifs que usávamos? Sumiram!!! Nosso código ficou bem mais simples e fácil de ler, basta você se acostumar com a API. ;)
Assista ao vídeo com a explicação passo a passo do exemplo, que você vai conseguir entender muito melhor.
Você já conhecia o Optional? Comente sobre o que achou da vídeo aula e artigo. :)





Olá,
o que você achou deste conteúdo? Conte nos comentários.