Eu tenho algum código que está falhando por causa de NullPointerException. Um método está sendo chamado no objeto em que o objeto não existe.
No entanto, isso me levou a pensar sobre a melhor maneira de corrigir isso. Eu sempre codifico defensivamente nulos para que eu possa codificar provas futuras para exceções de ponteiro nulo ou devo corrigir a causa do nulo para que não ocorra a jusante.
Quais são seus pensamentos?
Respostas:
Se null for um parâmetro de entrada razoável para o seu método, corrija o método. Caso contrário, corrija o chamador. "Razoável" é um termo flexível, por isso proponho o seguinte teste: Como o método deve manipular uma entrada nula? Se você encontrar mais de uma resposta possível, nulo não é uma entrada razoável.
fonte
Precondition.checkNotNull(...)
. Veja stackoverflow.com/questions/3022319/…IllegalArgumentException
quando for nulo. Isso sinaliza aos chamadores do método que o bug está em seu código (em vez de no próprio método).Não use null, use Optional
Como você apontou, um dos maiores problemas
null
do Java é que ele pode ser usado em qualquer lugar , ou pelo menos para todos os tipos de referência.É impossível dizer que poderia ser
null
e o que não poderia ser.Java 8 introduz um melhor padrão muito:
Optional
.E exemplo da Oracle:
Se cada um desses itens puder ou não retornar um valor bem-sucedido, você poderá alterar as APIs para
Optional
s:Ao codificar explicitamente a opção no tipo, suas interfaces ficarão muito melhores e seu código ficará mais limpo.
Se você não está usando o Java 8, pode ver no
com.google.common.base.Optional
Google Guava.Uma boa explicação da equipe do Guava: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained
Uma explicação mais geral das desvantagens de null, com exemplos de vários idiomas: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/
@Nonnull, @Nullable
O Java 8 adiciona essas anotações para ajudar as ferramentas de verificação de código como IDEs a detectar problemas. Eles são bastante limitados em sua eficácia.
Verifique quando faz sentido
Não escreva 50% do seu código marcando nulo, principalmente se não houver nada sensato que seu código possa fazer com um
null
valor.Por outro lado, se
null
puder ser usado e significar alguma coisa, certifique-se de usá-lo.Por fim, você obviamente não pode remover
null
do Java. Eu recomendo fortemente substituir aOptional
abstração sempre que possível e verificarnull
as outras vezes em que você pode fazer algo razoável sobre isso.fonte
NullPointerException
? ANullPointerException
pode acontecer literalmente toda vez que você chama um método de instância em Java. Você teriathrows NullPointerException
em quase todos os métodos de todos os tempos.Existem várias maneiras de lidar com isso:
if (obj != null) {}
não é ideal o uso de seu código , ele é confuso, adiciona ruído ao ler o código mais tarde durante o ciclo de manutenção e é propenso a erros, pois é fácil esquecer de fazer esse empacotamento da placa da caldeira.Depende se você deseja que o código continue executando silenciosamente ou falhe. É
null
um erro ou uma condição esperada.O que é nulo
Em todas as definições e casos, Null representa a absoluta falta de dados. Nulos nos bancos de dados representam a falta de um valor para essa coluna. um nulo
String
não é o mesmo que um vazioString
, um nuloint
não é a mesma coisa que ZERO, em teoria. Na prática "depende". VazioString
pode fazer uma boaNull Object
implementação para a classe String,Integer
pois depende da lógica de negócios.As alternativas são:
O
Null Object
padrão Crie uma instância do seu objeto que represente onull
estado e inicialize todas as referências a esse tipo com uma referência àNull
implementação. Isso é útil para objetos simples do tipo valor que não têm muitas referências a outros objetos que também poderiam sernull
e que se espera que sejamnull
um estado válido.Use as ferramentas Orientadas a aspecto para tecer métodos com um
Null Checker
aspecto que impeça que os parâmetros sejam nulos. Isto é para casos em quenull
há um erro.Use
assert()
não muito melhor que oif (obj != null){}
mas menos ruído.Use uma ferramenta de imposição de contrato, como Contracts For Java . O mesmo caso de uso que o AspectJ, mas mais recente e usa Anotações em vez de arquivos de configuração externos. Melhor dos dois trabalhos de Aspectos e afirmações.
1 é a solução ideal quando os dados recebidos são conhecidos
null
e precisam ser substituídos por algum valor padrão, para que os consumidores upstream não precisem lidar com todo o código nulo de verificação padrão. A verificação em relação aos valores padrão conhecidos também será mais expressiva.2, 3 e 4 são apenas geradores de exceção alternativos convenientes para substituir
NullPointerException
por algo mais informativo, que é sempre uma melhoria.No final
null
em Java é quase sempre um erro lógico. Você deve sempre se esforçar para eliminar a causa raiz doNullPointerExceptions
. Você deve se esforçar para não usarnull
condições como lógica de negócios.if (x == null) { i = someDefault; }
basta fazer a atribuição inicial a essa instância de objeto padrão.fonte
null
seja inesperado, então é um erro verdadeiro, então tudo deve parar completamente.null == null
(mesmo em PHP), mas nos bancos de dados,null != null
porque nos bancos de dados representa um valor desconhecido em vez de "nada". Duas incógnitas não são necessariamente iguais, enquanto duas nada são iguais.Adicionar verificações nulas pode tornar o teste problemático. Veja esta grande palestra ...
Confira a palestra de palestras do Google Tech: "O Código Limpo Fala - Não Procure Coisas!" ele fala sobre isso por minuto 24
http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E
A programação paranóica envolve adicionar verificações nulas em todos os lugares. Parece uma boa ideia a princípio, porém, de uma perspectiva de teste, torna difícil o seu tipo de verificação nula.
Além disso, quando você cria uma condição prévia para a existência de algum objeto, como
impede que você crie House, pois você lançará uma exceção. Suponha que seus casos de teste estejam criando objetos simulados para testar algo diferente de Door, bem, você não pode fazer isso porque a porta é necessária
Aqueles que sofreram com o inferno da criação trocista estão bem cientes desses tipos de aborrecimentos.
Em resumo, sua suíte de testes deve ser robusta o suficiente para testar portas, casas, telhados ou o que for, sem ter que ficar paranóica. Sério, quão difícil é adicionar um teste de verificação nula para objetos específicos em seus testes :)
Você sempre deve preferir aplicativos que funcionem porque você tem vários testes que PROVAM que ele funciona, em vez de ESPERAR que funciona simplesmente porque você tem várias verificações nulas pré-condições em todo o lugar
fonte
tl; dr - é BOM verificar
null
s inesperados, mas RUIM para um aplicativo tentar torná-los bons.Detalhes
Claramente, há situações em que
null
existe uma entrada ou saída válida para um método e outras em que não existe.Regra 1:
Regra # 2:
Dada uma especificação clara do "contrato" de um método vis-à-vis
null
s, é um erro de programação passar ou retornar umnull
onde você não deveria.Regra nº 3:
Se um método detectar um
null
que não deveria estar lá, ele não deve tentar corrigir o problema transformando-o em outra coisa. Isso apenas oculta o problema do programador. Em vez disso, deve permitir que o NPE aconteça e causar uma falha para que o programador possa descobrir qual é a causa raiz e corrigi-la. Esperamos que a falha seja notada durante o teste. Caso contrário, isso diz algo sobre sua metodologia de teste.Regra # 4:
Se você possui bugs no código que resultam em muitos NPEs, o mais difícil pode ser descobrir de onde
null
vieram os valores. Uma maneira de facilitar o diagnóstico é escrever seu código para que elenull
seja detectado o mais rápido possível. Geralmente, você pode fazer isso em conjunto com outras verificações; por exemplo(Obviamente, há casos em que as regras 3 e 4 devem ser temperadas com a realidade. Por exemplo (regra 3), alguns tipos de aplicativos devem tentar continuar após detectar provavelmente erros de programação. E (regra 4) pode haver muita verificação de parâmetros ruins um impacto no desempenho.)
fonte
Eu recomendaria corrigir o método para ser defensivo. Por exemplo:
Deve ser mais parecido com isto:
Sei que isso é absolutamente trivial, mas se o invocador espera que um objeto forneça um objeto padrão que não fará com que um nulo seja passado.
fonte
new String()
nada.null
s é a pior opção possível, pior do que lançar um NPE.As seguintes regras gerais sobre null me ajudaram muito até agora:
Se os dados vierem de fora do seu controle, verifique sistematicamente se há nulos e aja adequadamente. Isso significa lançar uma exceção que faz sentido para a função (marcada ou desmarcada, verifique se o nome da exceção informa exatamente o que está acontecendo). NUNCA, para evitar um valor frouxo no seu sistema que possa trazer surpresas.
Se Nulo estiver dentro do domínio de valores apropriados para o seu modelo de dados, lide com ele adequadamente.
Ao retornar valores, tente não retornar nulos sempre que possível. Sempre prefira listas vazias, cadeias vazias, padrões de objetos nulos. Mantenha nulos como valores retornados quando esta for a melhor representação possível de dados para um determinado caso de uso.
Provavelmente o mais importante de todos ... Testes, testes e testes novamente. Ao testar seu código, não o teste como codificador, teste-o como uma dominadora de psicopatas nazistas e tente imaginar todo tipo de maneira de torturar esse código.
Isso tende um pouco do lado paranóico em relação aos valores nulos, muitas vezes levando a fachadas e proxies que fazem a interface dos sistemas com o mundo externo e a valores estritamente controlados no interior com redundância abundante. O mundo exterior aqui significa praticamente tudo o que eu não codifiquei. Ele suporta um tempo de execução de custo, mas até agora raramente tive que otimizar isso criando "seções seguras nulas" de código. Devo dizer que, na maioria das vezes, crio sistemas de execução prolongada para cuidados de saúde, e a última coisa que desejo é que o subsistema de interface que transporta suas alergias ao iodo para o scanner de TC trava por causa de um ponteiro nulo inesperado, porque outra pessoa em outro sistema nunca percebeu que nomes poderiam conter apóstrofes ou caracteres como 但 耒耨。
de qualquer maneira .... meus 2 centavos
fonte
Eu proporia o uso do padrão Option / Some / None das linguagens funcionais. Não sou especialista em Java, mas uso intensivamente minha própria implementação desse padrão em meu projeto C # e tenho certeza de que ele pode ser convertido no mundo Java.
A idéia por trás desse padrão é: se houver uma situação lógica em que haja possibilidade de ausência de um valor (por exemplo, ao recuperar do banco de dados por ID), forneça um objeto do tipo Opção [T], onde T é um valor possível . I caso de ausência de um objeto de valor da classe None [T] retornou, caso a existência do valor - objeto de Some [T] retornasse, contendo o valor.
Nesse caso, você deve lidar com a possibilidade de ausência de valor e, se fizer uma revisão de código, poderá encontrar facilmente um tratamento incorreto. Para obter uma inspiração da implementação da linguagem C #, consulte meu repositório bitbucket https://bitbucket.org/mikegirkin/optionsomenone
Se você retornar um valor nulo e for logicamente equivalente a um erro (por exemplo, nenhum arquivo existe ou não pôde se conectar), você deve lançar uma exceção ou usar outro padrão de tratamento de erros. A idéia por trás disso, novamente, você acaba com a solução, quando precisa lidar com a situação de ausência de valor e pode encontrar facilmente os locais de manipulação incorreta no código.
fonte
Bem, se um dos possíveis resultados do seu método for um valor nulo, você deve codificar defensivamente para isso, mas se um método devolver um valor não nulo, mas não, eu resolveria isso com certeza.
Como sempre, depende do caso, como na maioria das coisas na vida :)
fonte
As bibliotecas lang do Apache Commons fornecem uma maneira de lidar com valores nulos
o método defaultIfNull na classe ObjectUtils permite retornar um valor padrão se o objeto passado for nulo
fonte
Não use o tipo Opcional, a menos que seja realmente opcional, geralmente a saída é melhor tratada como uma exceção, a menos que você realmente estivesse esperando nulo como uma opção, e não porque você escreve regularmente um código de buggy.
O problema não é nulo como um tipo que tem seus usos, como aponta o artigo do Google. O problema é que os nulos devem ser verificados e manipulados, geralmente podem ser retirados normalmente.
Há vários casos nulos que representam condições inválidas em áreas fora da operação rotineira do programa (entrada de usuário inválida, problemas no banco de dados, falha na rede, arquivos ausentes, dados corrompidos), e é para isso que servem as exceções verificadas, tratando-as, se é apenas para registrá-lo.
O tratamento de exceções é permitido diferentes prioridades e privilégios operacionais na JVM, em oposição a um tipo, como Opcional, que faz sentido para a natureza excepcional de sua ocorrência, incluindo suporte antecipado para carregamento lento e prioritário do manipulador na memória, desde que você não preciso dele andando o tempo todo.
Você não precisa escrever manipuladores nulos em todo o lugar, apenas onde eles provavelmente ocorrerem, em qualquer lugar em que você esteja acessando serviços de dados não confiáveis e, como a maioria deles se enquadra em poucos padrões gerais que podem ser facilmente abstraídos, você realmente só precisa uma chamada de manipulador, exceto em casos raros.
Portanto, acho que minha resposta seria envolvê-lo em uma exceção verificada e manipulá-lo ou corrigir o código não confiável, se estiver dentro de sua capacidade.
fonte