Eu tenho um método setter simples para uma propriedade e null
não é apropriado para essa propriedade em particular. Eu sempre fui rasgado nesta situação: devo jogar um IllegalArgumentException
, ou um NullPointerException
? Dos javadocs, ambos parecem apropriados. Existe algum tipo de padrão entendido? Ou isso é apenas uma daquelas coisas que você deve fazer o que preferir e as duas coisas estão realmente corretas?
547
IllegalArgumentException
está em contradição com o Java Objects.requireNonNull (T) e da goiaba Preconditions.checkNotNull (T) que joga umNullPointerException
. No entanto, a resposta certa é definitivamenteIllegalArgumentException
explicada na excelente resposta de Jason Cohen e na seção de comentários .Você deve usar
IllegalArgumentException
(IAE), nãoNullPointerException
(NPE) pelos seguintes motivos:Primeiro, o NPE JavaDoc lista explicitamente os casos em que o NPE é apropriado. Observe que todos eles são lançados pelo tempo de execução quando
null
usados inadequadamente. Por outro lado, o IAE JavaDoc não poderia ser mais claro: "Lançado para indicar que um método recebeu um argumento ilegal ou inapropriado". Sim, é você!Segundo, quando você vê um NPE em um rastreamento de pilha, o que você assume? Provavelmente alguém desreferenciou a
null
. Quando você vê o IAE, assume que o responsável pela chamada do método na parte superior da pilha passou com um valor ilegal. Novamente, a última suposição é verdadeira, a primeira é enganosa.Terceiro, como o IAE foi claramente projetado para validar parâmetros, você deve assumi-lo como a opção padrão de exceção. Então, por que você escolheria o NPE? Certamente não é para um comportamento diferente - você realmente espera que o código de chamada pegue os NPE separadamente do IAE e faça algo diferente como resultado? Você está tentando comunicar uma mensagem de erro mais específica? Mas você pode fazer isso no texto da mensagem de exceção de qualquer maneira, como faria com todos os outros parâmetros incorretos.
Quarto, todos os outros dados de parâmetros incorretos serão IAE; então, por que não ser consistente? Por que um ilegal
null
é tão especial que merece uma exceção separada de todos os outros tipos de argumentos ilegais?Por fim, aceito o argumento fornecido por outras respostas de que partes da API Java usam o NPE dessa maneira. No entanto, a API Java é inconsistente com tudo, desde tipos de exceção a convenções de nomenclatura, então acho que apenas copiar cegamente (sua parte favorita) a API Java não é um argumento suficientemente bom para superar essas outras considerações.
fonte
Validate.notNull
(commons lang) ePreconditions.checkNotNull
(goiaba), ambos do throw NPE :-(checkArgument(arg != null)
, apenas sem a conveniência de retornar o argumento, ou pode criar um utilitário local para o seu projeto. " code.google.com/p/guava-libraries/wiki/IdeaGraveyardO padrão é jogar o
NullPointerException
. O geralmente infalível "Java Efetivo" discute isso brevemente no Item 42 (primeira edição), Item 60 (segunda edição) ou Item 72 (terceira edição) "Favorecer o uso de exceções padrão":fonte
Eu era a favor de jogar
IllegalArgumentException
parâmetros nulos, até hoje, quando notei ojava.util.Objects.requireNonNull
método em Java 7. Com esse método, em vez de fazer:você pode fazer:
e lançará um
NullPointerException
se o parâmetro que você passar fornull
.Dado que esse método está bem no meio
java.util
, considero sua existência uma indicação bastante forte de que jogarNullPointerException
é "a maneira Java de fazer as coisas".Eu acho que sou decidido de qualquer forma.
Observe que os argumentos sobre depuração rígida são falsos, porque é claro que você pode fornecer uma mensagem
NullPointerException
dizendo o que era nulo e por que não deveria ser nulo. Assim como comIllegalArgumentException
.Uma vantagem adicional
NullPointerException
é que, em código crítico de alto desempenho, você pode dispensar uma verificação explícita de nulo (e umaNullPointerException
com uma mensagem de erro amigável) e confiar apenas noNullPointerException
que você obterá automaticamente quando chamar um método no nulo parâmetro. Desde que você chame um método rapidamente (ou seja, falhe rapidamente), você terá essencialmente o mesmo efeito, mas não tão amigável para o desenvolvedor. Na maioria das vezes, provavelmente é melhor verificar explicitamente e enviar com uma mensagem útil para indicar qual parâmetro foi nulo, mas é bom ter a opção de alterar isso se o desempenho exigir, sem violar o contrato publicado do método / construtor.fonte
Preconditions.checkNotNull(arg)
também lança NPE.Costumo seguir o design das bibliotecas JDK, especialmente Coleções e Concorrência (Joshua Bloch, Doug Lea, esses caras sabem como criar APIs sólidas). De qualquer forma, muitas APIs no JDK lançam proativamente
NullPointerException
.Por exemplo, o Javadoc para
Map.containsKey
estados:É perfeitamente válido lançar seu próprio NPE. A convenção é incluir o nome do parâmetro que foi nulo na mensagem da exceção.
O padrão é:
Faça o que fizer, não permita que um valor ruim seja definido e lance uma exceção mais tarde quando outro código tentar usá-lo. Isso torna a depuração um pesadelo. Você deve sempre seguir o princípio "fail-fast".
fonte
Votou o argumento de Jason Cohen porque foi bem apresentado. Deixe-me desmembrá-lo passo a passo. ;-)
O NPE JavaDoc diz explicitamente "outros usos ilegais do objeto nulo" . Se fosse limitado apenas a situações em que o tempo de execução encontrasse um nulo quando não deveria, todos esses casos poderiam ser definidos de maneira muito mais sucinta.
Não é possível evitá-lo se você assumir a coisa errada, mas assumindo que o encapsulamento foi aplicado corretamente, você realmente não deve se importar ou perceber se um nulo foi desreferenciado de forma inadequada versus se um método detectou um nulo inadequado e acionou uma exceção.
Eu escolheria o NPE ao invés do IAE por várias razões
Na verdade, outros argumentos inválidos podem resultar em todos os tipos de outras exceções. UnknownHostException , FileNotFoundException , uma variedade de exceções de erro de sintaxe, IndexOutOfBoundsException , falhas de autenticação, etc., etc.
Em geral, acho que o NPE é muito difamado, porque tradicionalmente tem sido associado a códigos que não seguem o princípio de falha rápida . Isso, além da falha do JDK em preencher os NPEs com uma sequência de mensagens, realmente criou um forte sentimento negativo que não é bem fundamentado. De fato, a diferença entre NPE e IAE da perspectiva do tempo de execução é estritamente o nome. Dessa perspectiva, quanto mais preciso você for o nome, mais clareza você dará ao chamador.
fonte
É uma questão de estilo "Guerra Santa". Em outras palavras, ambas as alternativas são boas, mas as pessoas terão suas preferências que defenderão até a morte.
fonte
NullPointerException
deve ser jogado: é a convenção de que os usos do JDK, e requer para interfaces, é mais específico (comoIndexOutOfBoundsException
, etc.), etc.Se é um
setter
método enull
está sendo passado para ele, acho que faria mais sentido lançar umIllegalArgumentException
. ANullPointerException
parece fazer mais sentido no caso em que você está tentando realmente usar onull
.Então, se você estiver usando-lo e é
null
,NullPointer
. Se ele está sendo passado e énull
,IllegalArgument
.fonte
O Apache Commons Lang tem uma NullArgumentException que executa várias das coisas discutidas aqui: estende IllegalArgumentException e seu único construtor leva o nome do argumento que deveria ter sido não nulo.
Embora eu ache que jogar algo como uma NullArgumentException ou IllegalArgumentException descreva com mais precisão as circunstâncias excepcionais, meus colegas e eu optamos por adiar os conselhos de Bloch sobre o assunto.
fonte
Não poderia concordar mais com o que está sendo dito. Falhe cedo, falhe rápido. Muito bom mantra de exceção.
A pergunta sobre qual exceção lançar é principalmente uma questão de gosto pessoal. Na minha opinião, IllegalArgumentException parece mais específico do que usar um NPE, pois está me dizendo que o problema foi com um argumento que eu passei para o método e não com um valor que pode ter sido gerado durante a execução do método.
Meus 2 centavos
fonte
Na verdade, a questão de lançar IllegalArgumentException ou NullPointerException é, na minha humilde visão, apenas uma "guerra santa" para uma minoria com um entendimento incomodo do tratamento de exceções em Java. Em geral, as regras são simples e da seguinte maneira:
Há pelo menos três razões muito boas contra o caso de mapear todos os tipos de violações de restrição de argumento para IllegalArgumentException, com a terceira provavelmente sendo tão severa a ponto de marcar o estilo inadequado da prática:
(1) Um programador não pode assumir com segurança que todos os casos de violações de restrição de argumento resultam em IllegalArgumentException, porque a grande maioria das classes padrão usa essa exceção em vez de uma cesta de lixo, se não houver um tipo mais específico de exceção disponível. Tentar mapear todos os casos de violações de restrição de argumento para IllegalArgumentException em sua API apenas leva à frustração do programador usando suas classes, pois as bibliotecas padrão geralmente seguem regras diferentes que violam as suas e a maioria dos usuários da API também as usa!
(2) O mapeamento das exceções realmente resulta em um tipo diferente de anomalia, causada por herança única: todas as exceções Java são classes e, portanto, suportam apenas herança única. Portanto, não há como criar uma exceção que seja realmente uma NullPointerException e uma IllegalArgumentException, pois as subclasses só podem herdar de uma ou de outra. Lançar uma IllegalArgumentException no caso de um argumento nulo torna mais difícil para os usuários da API distinguir entre problemas sempre que um programa tenta corrigir programaticamente o problema, por exemplo, alimentando valores padrão em uma repetição de chamada!
(3) O mapeamento realmente cria o perigo de mascarar erros: para mapear violações de restrição de argumento em IllegalArgumentException, você precisará codificar uma tentativa externa de captura dentro de cada método que possua argumentos restritos. No entanto, simplesmente capturar RuntimeException nesse bloco de captura está fora de questão, porque esse risco corre o mapeamento de RuntimeExceptions documentados lançados pelos métodos de libery usados dentro do seu em IllegalArgumentException, mesmo que eles não sejam causados por violações de restrição de argumento. Portanto, você precisa ser muito específico, mas mesmo esse esforço não o protege do caso de mapear acidentalmente uma exceção de tempo de execução não documentada de outra API (ou seja, um bug) em uma IllegalArgumentException da sua API.
Por outro lado, com a prática padrão, as regras permanecem simples e as causas de exceção permanecem desmascaradas e específicas. Para o responsável pela chamada do método, as regras também são fáceis: - se você encontrar uma exceção de tempo de execução documentada de qualquer tipo porque passou um valor ilegal, repita a chamada com um padrão (pois essas exceções específicas são necessárias) ou corrija seu código - se, por outro lado, você encontrar uma exceção de tempo de execução que não está documentada para um determinado conjunto de argumentos, envie um relatório de bug aos criadores do método para garantir que seu código ou sua documentação seja corrigida.
fonte
A prática aceita se você usar a IllegalArgumentException (String message) para declarar um parâmetro inválido e fornecer o máximo de detalhes possível ... Por assim dizer, um parâmetro foi considerado nulo enquanto a exceção não nula, você faria algo como isso:
Você não tem praticamente nenhum motivo para usar implicitamente a "NullPointerException". O NullPointerException é uma exceção lançada pela Java Virtual Machine quando você tenta executar o código na referência nula (como toString () ).
fonte
Lançando uma exceção exclusiva para
null
argumentos (sejaNullPointerException
um tipo personalizado) torna onull
teste automatizado mais confiável. Esse teste automatizado pode ser feito com reflexão e um conjunto de valores predefinidos, como em goiaba s'NullPointerTester
. Por exemplo,NullPointerTester
tentaria chamar o seguinte método ...... com duas listas de argumentos:
"", null
enull, ImmutableList.of()
. Testaria se cada uma dessas chamadas gera o esperadoNullPointerException
. Para esta implementação, passar umanull
lista não produzNullPointerException
. No entanto, produz umIllegalArgumentException
porqueNullPointerTester
usa uma sequência padrão de""
. E seNullPointerTester
espera apenasNullPointerException
paranull
valores, ele pega o bug. Se esperaIllegalArgumentException
, perde.fonte
Algumas coleções assumem que
null
é rejeitado usando emNullPointerException
vez deIllegalArgumentException
. Por exemplo, se você comparar um conjunto que contémnull
um conjunto que rejeitanull
, o primeiro conjunto chamacontainsAll
o outro e captura seuNullPointerException
- mas nãoIllegalArgumentException
. (Estou olhando para a implementação deAbstractSet.equals
.)Você poderia argumentar razoavelmente que o uso de exceções não verificadas dessa maneira é um antipadrão, que comparar coleções que contêm
null
com coleções que não podem conternull
é um bug provável que realmente deve gerar uma exceção ou que a criaçãonull
de uma coleção é uma má ideia. . No entanto, a menos que você esteja disposto a dizer que issoequals
deve gerar uma exceção nesse caso, você ficará paralisado ao lembrar queNullPointerException
é necessário em determinadas circunstâncias, mas não em outras. ("IAE antes do NPE, exceto depois de 'c' ...")fonte
new TreeSet<>().containsAll(Arrays.asList((Object) null));
jogaNPE
porque oList
contémnull
.Como uma questão subjetiva, isso deve ser fechado, mas como ainda está aberto:
Isso faz parte da política interna usada no meu local de trabalho anterior e funcionou muito bem. Isso tudo é da memória, então não consigo me lembrar da letra exata. Vale ressaltar que eles não usaram exceções verificadas, mas isso está além do escopo da pergunta. As exceções não verificadas que eles usaram caíram em 3 categorias principais.
NullPointerException: não atire intencionalmente. Os NPEs devem ser lançados apenas pela VM ao remover uma referência nula. Todo esforço possível deve ser feito para garantir que eles nunca sejam lançados. @Nullable e @NotNull devem ser usados em conjunto com as ferramentas de análise de código para encontrar esses erros.
IllegalArgumentException: lançado quando um argumento para uma função não está em conformidade com a documentação pública, de modo que o erro possa ser identificado e descrito em termos dos argumentos passados. A situação do OP se enquadra nessa categoria.
IllegalStateException: Lançada quando uma função é chamada e seus argumentos são inesperados no momento em que são passados ou incompatíveis com o estado do objeto do qual o método é membro.
Por exemplo, havia duas versões internas da IndexOutOfBoundsException usadas em itens que tinham um comprimento. Uma subclasse de IllegalStateException, usada se o índice for maior que o comprimento. A outra, uma subclasse de IllegalArgumentException, usada se o índice for negativo. Isso ocorreu porque você poderia adicionar mais itens ao objeto e o argumento seria válido, enquanto um número negativo nunca é válido.
Como eu disse, esse sistema funciona muito bem e foi preciso alguém para explicar por que a distinção existe: "Dependendo do tipo de erro, é bastante simples você descobrir o que fazer. Mesmo que você não consiga realmente descobrir Para descobrir o que deu errado, você pode descobrir onde detectar esse erro e criar informações adicionais de depuração ".
NullPointerException: manipule o caso Null ou insira uma asserção para que o NPE não seja lançado. Se você colocar uma afirmação, é apenas um dos outros dois tipos. Se possível, continue a depuração como se a afirmação estivesse lá em primeiro lugar.
IllegalArgumentException: você tem algo errado no site de chamadas. Se os valores transmitidos forem de outra função, descubra por que você está recebendo um valor incorreto. Se você estiver passando um de seus argumentos, propague o erro verifica a pilha de chamadas até encontrar a função que não está retornando o que você espera.
IllegalStateException: você não chamou suas funções na ordem correta. Se você estiver usando um de seus argumentos, verifique-os e gere uma IllegalArgumentException descrevendo o problema. Você pode propagar as bochechas contra a pilha até encontrar o problema.
De qualquer forma, o argumento dele era que você só pode copiar o IllegalArgumentAssertions na pilha. Não há como você propagar IllegalStateExceptions ou NullPointerExceptions na pilha porque eles têm algo a ver com a sua função.
fonte
Em geral, um desenvolvedor nunca deve lançar uma NullPointerException. Essa exceção é lançada pelo tempo de execução quando o código tenta desreferenciar uma variável cujo valor é nulo. Portanto, se o seu método deseja proibir explicitamente nulo, em vez de apenas um valor nulo aumentar uma NullPointerException, você deve lançar uma IllegalArgumentException.
fonte
Queria destacar argumentos nulos de outros argumentos ilegais; portanto, derivei uma exceção do IAE chamada NullArgumentException. Mesmo sem precisar ler a mensagem de exceção, sei que um argumento nulo foi passado para um método e, lendo a mensagem, descubro qual argumento foi nulo. Eu ainda pego o NullArgumentException com um manipulador IAE, mas nos meus logs é onde posso ver a diferença rapidamente.
fonte
a dicotomia ... Eles não se sobrepõem? Somente partes não sobrepostas de um todo podem fazer uma dicotomia. Da maneira que eu vejo:
fonte
NullPointerException
não faria nada. A única coisa que poderia ajudar éIllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException
, mas não temos herança múltipla.NullPointerException
lançada ao tentar acessar um objeto com uma variável de referência cujo valor atual énull
.IllegalArgumentException
lançado quando um método recebe um argumento formatado de forma diferente do que o método espera.fonte
De acordo com o seu cenário,
IllegalArgumentException
é a melhor escolha, porquenull
não é um valor válido para sua propriedade.fonte
Idealmente, as exceções de tempo de execução não devem ser lançadas. Uma exceção marcada (exceção comercial) deve ser criada para o seu cenário. Como se qualquer uma dessas exceções for lançada e registrada, ela desviará o desenvolvedor ao passar pelos logs. Em vez disso, as exceções de negócios não criam esse pânico e geralmente são ignoradas durante a solução de problemas dos logs.
fonte
As definições dos links para as duas exceções acima são IllegalArgumentException: Lançadas para indicar que um método recebeu um argumento ilegal ou inapropriado. NullPointerException: Lançado quando um aplicativo tenta usar null em um caso em que um objeto é necessário.
A grande diferença aqui é que o IllegalArgumentException deve ser usado ao verificar se um argumento para um método é válido. NullPointerException deve ser usado sempre que um objeto está sendo "usado" quando é nulo.
Espero que ajude a colocar os dois em perspectiva.
fonte
Se for um "setter", ou em algum lugar em que eu vou conseguir que um membro use mais tarde, costumo usar IllegalArgumentException.
Se for algo que eu vou usar (desreferencia) agora no método, eu lanço um NullPointerException proativamente. Eu gosto mais disso do que deixar o tempo de execução fazê-lo, porque posso fornecer uma mensagem útil (parece que o tempo de execução também pode fazer isso, mas isso é um discurso retórico por mais um dia).
Se estou substituindo um método, utilizo o que o método substituído usar.
fonte
Você deve lançar uma IllegalArgumentException, pois tornará óbvio para o programador que ele fez algo inválido. Os desenvolvedores estão tão acostumados a ver o NPE lançado pela VM, que qualquer programador não perceberia imediatamente seu erro e começaria a procurar aleatoriamente, ou pior, culpar seu código por ser 'buggy'.
fonte
Nesse caso, IllegalArgumentException transmite informações claras ao usuário usando sua API que "não deve ser nulo". Como outros usuários do fórum apontaram, você pode usar o NPE se desejar transmitir as informações corretas ao usuário usando sua API.
GaryF e tweakt eliminaram as referências "Effective Java" (pelo que juro) que recomendam o uso do NPE. E observar como outras boas APIs são construídas é a melhor maneira de ver como construir sua API.
Outro bom exemplo é examinar as APIs do Spring. Por exemplo, org.springframework.beans.BeanUtils.instantiateClass (construtor ctor, Object [] args) possui uma linha Assert.notNull (ctor, "O construtor não deve ser nulo"). O método org.springframework.util.Assert.notNull (objeto Objeto, mensagem String) verifica se o argumento (objeto) passado é nulo e, se for, lança uma nova IllegalArgumentException (mensagem) que é capturada na organização. método springframework.beans.BeanUtils.instantiateClass (...).
fonte
Se você optar por lançar um NPE e estiver usando o argumento em seu método, pode ser redundante e caro verificar explicitamente se existe um nulo. Eu acho que a VM já faz isso por você.
fonte
NPE
ainda; os programadores falamIAE
antes da VM, se quiserem.