O NullPointerException é provavelmente o erro mais famoso do Java. Ele acontece quando você tenta usar um objeto que, na verdade, é null:
Cliente cliente = repositorio.buscarPorEmail("ana@exemplo.com"); // devolveu null
String cidade = cliente.getEndereco().getCidade(); // 💥 NullPointerExceptionO problema não é o null em si, e sim que nada no código avisava que ele podia aparecer. Desde o Java 14, a mensagem de erro ao menos diz qual parte da expressão era nula — mas o ideal é que o erro nem aconteça.
O que é Optional
Optional<T> é uma “caixa” que pode conter um valor ou estar vazia. Quando um método devolve Optional, ele está dizendo, no próprio tipo: “talvez não haja resultado — trate isso”.
public Optional<Cliente> buscarPorEmail(String email) {
return clientes.stream()
.filter(c -> c.getEmail().equals(email))
.findFirst();
}No Spring Data JPA você já usa isso: findById devolve Optional.
Como tirar o valor da caixa
Optional<Cliente> resultado = repositorio.buscarPorEmail(email);
// valor padrão
Cliente c1 = resultado.orElse(Cliente.visitante());
// valor padrão calculado só se precisar
Cliente c2 = resultado.orElseGet(() -> criarVisitante());
// lançar uma exceção com significado
Cliente c3 = resultado.orElseThrow(() -> new ClienteNaoEncontradoException(email));
// executar algo só se existir
resultado.ifPresent(c -> enviarBoasVindas(c));Encadeando com map
O exemplo do início, aquele que explodia, fica seguro assim:
String cidade = repositorio.buscarPorEmail(email)
.map(Cliente::getEndereco)
.map(Endereco::getCidade)
.orElse("Cidade não informada");Se qualquer etapa estiver vazia, o resto é ignorado e cai no orElse.
Erros comuns com Optional
- Chamar
get()sem verificar. Isso só troca o NPE por umNoSuchElementException. PrefiraorElseThrow(). - Usar
Optionalem atributos ou parâmetros. Ele foi pensado para retornos de métodos. - Devolver
nullnum método que retornaOptional. UseOptional.empty(). - Embrulhar coleções. Em vez de
Optional<List<T>>, devolva uma lista vazia.
Optional. Se um valor nunca deveria ser nulo, valide cedo com Objects.requireNonNull.