O problema que Optional resolve
Um método que talvez não tenha uma resposta sempre teve uma escolha desconfortável em Java: retornar null e torcer para quem o chama lembrar de verificar. Geralmente não lembra, e o resultado é um NullPointerException que aparece bem longe do método que retornou o null.
Optional<T> é uma pequena caixa que ou contém um valor ou está explicitamente vazia. Ao retornar Optional<User> em vez de User, um método diz a quem o chama "isso pode não encontrar nada" na própria assinatura - e o compilador o conduz a tratar esse caso.
Criando um Optional
Há três métodos de fábrica, e escolher o certo importa:
Optional.of(value)- o valor precisa ser não nulo, ou ele lançaNullPointerExceptionna hora.Optional.ofNullable(value)- vazio se o valor for null, presente caso contrário. Use para envolver algo que pode ser null.Optional.empty()- um optional vazio.
Um erro comum é recorrer a Optional.of(x) em um valor que pode ser null - isso anula o propósito e lança exatamente a exceção que você tentava evitar. Na dúvida, use ofNullable.
Lendo o valor com segurança
Uma vez que você tem um optional, o objetivo é tirar o valor sem presumir que ele está lá. A ferramenta bruta é get(), e você quase nunca deveria usá-la:
Optional<String> name = Optional.empty();
String value = name.get(); // lança NoSuchElementException - um NPE disfarçado
Em vez disso, forneça um valor de fallback ou reaja à presença. orElse retorna um padrão quando vazio; ifPresent executa código apenas quando um valor existe:
Use orElseGet(supplier) em vez de orElse quando o padrão for caro de construir - o supplier só executa se o optional estiver de fato vazio. O argumento de orElse é sempre avaliado, mesmo quando não é necessário.
orElseThrow para "isso deveria existir"
Às vezes o vazio realmente é um erro - um valor de configuração obrigatório, um usuário que deveria estar no banco de dados. orElseThrow transforma um optional vazio em uma exceção clara da sua escolha:
Isso se lê muito melhor do que um bloco if (opt.isPresent()) e torna a falha explícita no ponto em que ela acontece.
Transformando com map e filter
O verdadeiro ganho é o encadeamento. map aplica uma função ao valor somente se presente e deixa um optional vazio vazio - então você transforma sem nunca tocar em um null. filter descarta o valor se ele não passar em um teste.
Se sua função de mapeamento retornar ela mesma um Optional, use flatMap em vez de map para evitar um desconfortável Optional<Optional<T>>. Isso espelha a distinção map/flatMap que você viu com streams - um Optional se comporta muito como um stream de zero ou um elemento.
Onde Optional se encaixa (e onde não)
Optional foi projetado como tipo de retorno para métodos que legitimamente podem não produzir um resultado - os exemplos canônicos são Stream.findFirst(), buscas no estilo Map e parsing. Use-o ali e sua API documenta as próprias lacunas.
Ele não é feito para:
- Campos. Não é serializável e adiciona um objeto por campo. Use referências comuns e valide no construtor.
- Parâmetros de método. Quem chama então tem que envolver os argumentos em
Optional.of(...), o que é mais ruidoso do que uma sobrecarga ou um parâmetro que aceita null. - Coleções. Retorne uma
Listvazia, não umOptional<List>. Uma coleção vazia já significa "nada".
Tratado como tipo de retorno, Optional transforma bugs silenciosos de null em avisos em tempo de compilação para tratar o caso ausente.
Próximo: Exceções
Optional trata com elegância o caso cotidiano de "pode não haver valor", mas algumas falhas são genuinamente excepcionais - um arquivo que não abre, uma rede que cai, uma entrada que não pode ser parseada. Java modela esses casos com exceções, e essa é a próxima página.
Perguntas frequentes
O que é Optional em Java e por que usá-lo?
Optional<T> é um contêiner que ou guarda um valor ou está vazio. Ele existe para deixar explícito no tipo de retorno de um método que "pode não haver valor", de modo que quem o chama é incentivado a tratar o caso vazio em vez de esquecer uma verificação de null e topar com um NullPointerException em tempo de execução. Use-o principalmente como tipo de retorno de métodos que podem não produzir um resultado, como uma busca que não encontra nada.
Qual é a diferença entre Optional.of e Optional.ofNullable?
Optional.of(x) lança NullPointerException na hora se x for null - use-o quando você sabe que o valor não é null. Optional.ofNullable(x) retorna um Optional vazio quando x é null e um presente caso contrário - use-o quando o valor pode ser null. Optional.empty() te dá um optional vazio diretamente.
Devo chamar Optional.get() para ler o valor?
Evite o get() direto - ele lança NoSuchElementException se o optional estiver vazio, o que é só um NullPointerException com outro nome. Prefira orElse, orElseGet, orElseThrow, ifPresent ou map para que o caso vazio seja sempre tratado. Se precisar verificar antes, proteja com isPresent(), mas os métodos funcionais são mais limpos.