Ao ler o código-fonte JDK, acho comum que o autor verifique os parâmetros se eles forem nulos e, em seguida, jogue nova NullPointerException () manualmente. Por que eles fazem isso? Eu acho que não há necessidade de fazer isso, pois ele lançará um novo NullPointerException () quando ele chama qualquer método. (Aqui está um código-fonte do HashMap, por exemplo :)
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
java
nullpointerexception
LiJiaming
fonte
fonte
ArgumentNullException
em casos como esse (em vez deNullReferenceException
) - é realmente uma boa pergunta sobre por que você levantariaNullPointerException
explicitamente aqui (em vez de outro).IllegalArgumentException
ouNullPointerException
para um argumento nulo. A convenção JDK é a última.Respostas:
Há várias razões que vêm à mente, sendo várias relacionadas de perto:
Fail-fast: se falhar, é melhor falhar mais cedo ou mais tarde. Isso permite que os problemas sejam capturados mais perto de sua origem, facilitando sua identificação e recuperação. Também evita o desperdício de ciclos da CPU em códigos que provavelmente falharão.
Intenção: lançar a exceção explicitamente deixa claro para os mantenedores que o erro existe intencionalmente e o autor estava ciente das consequências.
Consistência: se fosse permitido que o erro ocorresse naturalmente, ele pode não ocorrer em todos os cenários. Se nenhum mapeamento for encontrado, por exemplo,
remappingFunction
nunca seria usado e a exceção não seria lançada. A validação antecipada de entradas permite um comportamento mais determinístico e uma documentação mais clara .Estabilidade: o código evolui com o tempo. Código que encontra uma exceção naturalmente pode, após um pouco de refatoração, deixar de fazê-lo ou em circunstâncias diferentes. Jogá-lo explicitamente torna menos provável que o comportamento mude inadvertidamente.
fonte
new NullPointerException(message)
construtor para esclarecer o que é nulo. Bom para pessoas que não têm acesso ao seu código-fonte. Eles até fizeram disso uma linha no JDK 8 com oObjects.requireNonNull(object, message)
método utilitário.É para maior clareza, consistência e para impedir a execução de trabalhos extras e desnecessários.
Considere o que aconteceria se não houvesse uma cláusula de guarda na parte superior do método. Sempre chamava
hash(key)
egetNode(hash, key)
mesmo quandonull
tinha sido passadoremappingFunction
antes do NPE ser lançado.Pior ainda, se a
if
condição for,false
então pegamos oelse
branch, que não usa oremappingFunction
mesmo, o que significa que o método nem sempre lança NPE quando anull
é passada; se depende depende do estado do mapa.Ambos os cenários são ruins. Se
null
não for um valor válido,remappingFunction
o método lançará uma exceção de forma consistente, independentemente do estado interno do objeto no momento da chamada, e deve ser feito sem realizar trabalhos desnecessários que não fazem sentido, uma vez que serão lançados. Finalmente, é um bom princípio de código limpo e claro ter a guarda na frente, para que qualquer pessoa que revise o código-fonte possa ver prontamente que o fará.Mesmo que a exceção tenha sido lançada por todos os ramos do código, é possível que uma revisão futura do código mude isso. A realização da verificação no início garante que ela será definitivamente executada.
fonte
Além dos motivos listados pela excelente resposta de @ shmosel ...
Desempenho: pode haver / houve benefícios de desempenho (em algumas JVMs) em lançar o NPE explicitamente em vez de permitir que a JVM faça isso.
Depende da estratégia que o interpretador Java e o compilador JIT adotam para detectar a desreferenciação de ponteiros nulos. Uma estratégia é não testar nulo, mas, em vez disso, interceptar o SIGSEGV que ocorre quando uma instrução tenta acessar o endereço 0. Essa é a abordagem mais rápida no caso em que a referência é sempre válida, mas é cara no caso do NPE.
Um teste explícito
null
no código evitaria o desempenho do SIGSEGV em um cenário em que os NPEs eram frequentes.(Duvido que isso seja uma micro otimização interessante em uma JVM moderna, mas poderia ter sido no passado.)
Compatibilidade: O motivo provável de não haver mensagem na exceção é a compatibilidade com os NPEs lançados pela própria JVM. Em uma implementação Java compatível, um NPE lançado pela JVM possui uma
null
mensagem. (Android Java é diferente.)fonte
Além do que outras pessoas apontaram, vale a pena notar o papel da convenção aqui. Em C #, por exemplo, você também tem a mesma convenção de criar explicitamente uma exceção em casos como esse, mas é especificamente um
ArgumentNullException
, que é um pouco mais específico. (A convenção em C # é queNullReferenceException
sempre representa algum tipo de bug - simplesmente, isso nunca deveria acontecer no código de produção; concedido,ArgumentNullException
geralmente acontece também, mas poderia ser um bug mais na linha de "você não entender como usar a biblioteca corretamente "(tipo de bug).Então, basicamente, em C #
NullReferenceException
significa que seu programa realmente tentou usá-lo, ao passoArgumentNullException
que reconheceu que o valor estava errado e nem se deu ao trabalho de tentar usá-lo. As implicações podem realmente ser diferentes (dependendo das circunstâncias) porqueArgumentNullException
significa que o método em questão ainda não teve efeitos colaterais (uma vez que falhou nas condições prévias do método).Aliás, se você está levantando algo parecido com
ArgumentNullException
ouIllegalArgumentException
, isso faz parte do objetivo de fazer a verificação: você deseja uma exceção diferente da que "normalmente" recebe.De qualquer maneira, o aumento explícito da exceção reforça a boa prática de ser explícito sobre as pré-condições e os argumentos esperados do seu método, o que facilita a leitura, o uso e a manutenção do código. Se você não procurou explicitamente
null
, não sei se é porque você pensou que ninguém jamais passaria umanull
discussão, você está contando para lançar a exceção de qualquer maneira, ou simplesmente esqueceu de verificar.fonte
null
objeto". Não sou louco por isso, mas esse parece ser o design pretendido.NullReferenceException
(equivalente aNullPointerException
) significa que seu código realmente tentou usá- lo (o que é sempre um bug - nunca deve acontecer no código de produção), vs. "Eu sei que o argumento está errado, então nem sequer tente usá-lo. " Há tambémArgumentException
(o que significa que o argumento estava errado por algum outro motivo).É assim que você receberá a exceção assim que cometer o erro, e mais tarde quando estiver usando o mapa e não entenderá por que isso aconteceu.
fonte
Transforma uma condição de erro aparentemente irregular em uma violação clara do contrato: a função possui algumas condições prévias para funcionar corretamente; portanto, as verifica previamente, fazendo com que sejam cumpridas.
O efeito é que você não precisará depurar
computeIfPresent()
ao obter a exceção. Depois de ver que a exceção vem da verificação de pré-condição, você sabe que chamou a função com um argumento ilegal. Se a verificação não estivesse lá, você precisaria excluir a possibilidade de que haja algum bug emcomputeIfPresent()
si que leve à exceção.Obviamente, jogar o genérico
NullPointerException
é uma péssima escolha, pois não indica uma violação do contrato por si só.IllegalArgumentException
seria uma escolha melhor.Nota:
Não sei se o Java permite isso (duvido), mas os programadores de C / C ++ usam um
assert()
neste caso, o que é significativamente melhor para a depuração: ele diz ao programa para travar imediatamente e com a maior força possível, caso necessário. condição avaliada como falsa. Então, se você correusob um depurador, e algo passado
NULL
para qualquer argumento, o programa parava na linha dizendo qual era o argumentoNULL
e você podia examinar todas as variáveis locais de toda a pilha de chamadas à vontade.fonte
assert something != null;
mas isso requer a-assertions
sinalização ao executar o aplicativo. Se a-assertions
bandeira não está lá, a palavra-chave assert não irá lançar uma AssertionExceptionÉ porque é possível que isso não aconteça naturalmente. Vamos ver um código como este:
(é claro que existem inúmeros problemas neste exemplo sobre a qualidade do código. Desculpe a preguiça de imaginar um exemplo perfeito)
Situação em
c
que não será realmente usada eNullPointerException
não será lançada, mesmo quec == null
seja possível.Em situações mais complicadas, torna-se muito fácil caçar esses casos. É por isso que a verificação geral
if (c == null) throw new NullPointerException()
é melhor.fonte
É intencional proteger mais danos ou entrar em estado inconsistente.
fonte
Além de todas as outras excelentes respostas aqui, também gostaria de adicionar alguns casos.
Você pode adicionar uma mensagem se criar sua própria exceção
Se você jogar o seu próprio,
NullPointerException
poderá adicionar uma mensagem (que você definitivamente deveria!)A mensagem padrão é
null
denew NullPointerException()
e todos os métodos que a utilizam, por exemploObjects.requireNonNull
. Se você imprimir esse nulo, pode até traduzir para uma sequência vazia ...Um pouco curto e pouco informativo ...
O rastreamento da pilha fornecerá muitas informações, mas, para que o usuário saiba o que foi nulo, é necessário desenterrar o código e observar a linha exata.
Agora imagine que o NPE está sendo encapsulado e enviado pela rede, por exemplo, como uma mensagem em um erro de serviço da web, talvez entre diferentes departamentos ou até organizações. Na pior das hipóteses, ninguém pode descobrir o que
null
significa ...As chamadas de método em cadeia o manterão adivinhando
Uma exceção informará apenas em qual linha a exceção ocorreu. Considere a seguinte linha:
Se você obtiver um NPE e ele apontar para esta linha, qual
repository
esomeObject
era nulo?Em vez disso, verificar essas variáveis quando você as obtiver apontará pelo menos para uma linha onde, esperamos, elas sejam a única variável sendo manipulada. E, como mencionado anteriormente, ainda melhor se sua mensagem de erro contiver o nome da variável ou similar.
Erros ao processar muitas entradas devem fornecer informações de identificação
Imagine que seu programa está processando um arquivo de entrada com milhares de linhas e de repente existe uma NullPointerException. Você olha para o local e percebe que alguma entrada estava incorreta ... que entrada? Você precisará de mais informações sobre o número da linha, talvez a coluna ou até todo o texto da linha para entender qual linha desse arquivo precisa ser corrigida.
fonte