O motivo Optional
foi adicionado ao Java é porque este:
return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.findFirst()
.getOrThrow(() -> new InternalError(...));
é mais limpo que isso:
Method matching =
Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.getFirst();
if (matching == null)
throw new InternalError("Enclosing method not found");
return matching;
Meu argumento é que o Optional foi escrito para suportar a programação funcional , que foi adicionada ao Java ao mesmo tempo. (O exemplo é cortesia de um blog de Brian Goetz . Um exemplo melhor pode usar o orElse()
método, pois esse código gera uma exceção de qualquer maneira, mas você entendeu.)
Mas agora, as pessoas estão usando o Opcional por um motivo muito diferente. Eles o estão usando para solucionar uma falha no design da linguagem. A falha é esta: não há como especificar quais parâmetros e valores de retorno de uma API podem ser nulos. Isso pode ser mencionado nos javadocs, mas a maioria dos desenvolvedores nem escreve javadocs para seu código, e muitos não verificam os javadocs enquanto escrevem. Portanto, isso leva a muito código que sempre verifica valores nulos antes de usá-los, mesmo que eles geralmente não possam ser nulos porque já foram validados repetidamente nove ou dez vezes na pilha de chamadas.
Acho que havia uma verdadeira sede de solucionar essa falha, porque muitas pessoas que viram a nova classe Opcional assumiram que seu objetivo era adicionar clareza às APIs. É por isso que as pessoas fazem perguntas como "os getters devem retornar opcionais?" Não, provavelmente não deveriam, a menos que você espere que o getter seja usado na programação funcional, o que é muito improvável. De fato, se você observar onde o Optional é usado na API Java, é principalmente nas classes Stream, que são o núcleo da programação funcional. (Não verifiquei muito bem, mas as classes Stream podem ser o único local em que são usadas.)
Se você planeja usar um getter em um pouco de código funcional, pode ser uma boa idéia ter um getter padrão e um segundo que retorne Opcional.
Ah, e se você precisar que sua classe seja serializável, não use absolutamente Opcional.
Os opcionais são uma solução muito ruim para a falha da API porque: a) são muito detalhados eb) nunca foram destinados a resolver esse problema.
Uma solução muito melhor para a falha da API é o Verificador de Nulidade . Este é um processador de anotação que permite especificar quais parâmetros e valores de retorno podem ser nulos, anotando-os com @Nullable. Dessa maneira, o compilador pode varrer o código e descobrir se um valor que pode ser realmente nulo está sendo passado para um valor em que nulo não é permitido. Por padrão, ele assume que nada pode ser nulo, a menos que seja anotado. Dessa forma, você não precisa se preocupar com valores nulos. Passar um valor nulo para um parâmetro resultará em um erro do compilador. Testar um objeto para null que não pode ser nulo produz um aviso do compilador. O efeito disso é alterar o NullPointerException de um erro de tempo de execução para um erro em tempo de compilação.
Isso muda tudo.
Quanto aos seus colaboradores, não use Opcional. E tente criar suas classes para que nenhum dos membros possa ser nulo. E talvez tente adicionar o Verificador de Nulidade ao seu projeto e declarar seus getters e parâmetros de setter @Nullable, se necessário. Eu só fiz isso com novos projetos. Provavelmente produz muitos avisos em projetos existentes escritos com muitos testes supérfluos para nulo; portanto, pode ser difícil atualizar. Mas também vai pegar muitos bugs. Eu amo isso. Meu código é muito mais limpo e mais confiável por causa disso.
(Também há uma nova linguagem que trata disso. O Kotlin, que é compilado com o código de bytes Java, permite especificar se um objeto pode ser nulo quando você o declara. É uma abordagem mais limpa.)
Adenda à postagem original (versão 2)
Depois de pensar muito, cheguei à conclusão de que é aceitável retornar Opcional com uma condição: que o valor recuperado possa realmente ser nulo. Eu tenho visto muitos códigos em que as pessoas rotineiramente retornam Opcional de getters que não podem retornar nulo. Eu vejo isso como uma prática de codificação muito ruim, que apenas adiciona complexidade ao código, o que aumenta a probabilidade de erros. Mas quando o valor retornado puder ser realmente nulo, vá em frente e envolva-o em um Opcional.
Lembre-se de que os métodos projetados para programação funcional e que exigem uma referência de função serão (e devem) escritos em duas formas, uma das quais usa Opcional. Por exemplo, Optional.map()
e Optional.flatMap()
ambos aceitam referências de função. O primeiro faz uma referência a um getter comum e o segundo leva um que retorna Opcional. Portanto, você não está fazendo um favor a ninguém retornando um Opcional em que o valor não pode ser nulo.
Dito tudo isso, ainda vejo que a abordagem usada pelo Nullness Checker é a melhor maneira de lidar com nulos, pois eles transformam NullPointerExceptions de bugs de tempo de execução para compilar erros de tempo.