Pergunta

Eu tenho um método setter simples para uma propriedade e null não é apropriado para esta propriedade específica.Sempre fiquei dividido nesta situação:devo jogar um IllegalArgumentException, ou um NullPointerException?Dos javadocs, ambos parecem apropriados.Existe algum tipo de padrão compreendido?Ou esta é apenas uma daquelas coisas que você deve fazer o que preferir e ambas estão realmente corretas?

Foi útil?

Solução

Parece um IllegalArgumentException é necessário se você não quiser null ser um valor permitido, e o NullPointerException seria jogado se você estivesse tentando usar uma variável que acaba sendo null.

Outras dicas

Você deveria estar usando IllegalArgumentException (IAE), não NullPointerException (NPE) pelos seguintes motivos:

Primeiro, o JavaDoc NPE lista explicitamente os casos em que o NPE é apropriado.Observe que todos eles são lançados pelo tempo de execução quando null é usado de forma inadequada.Em contrapartida, o IAE JavaDoc não poderia ser mais claro:"Jogado para indicar que um método foi aprovado em um argumento ilegal ou inadequado". Sim, é você!

Segundo, quando você vê um NPE em um rastreamento de pilha, o que você presume?Provavelmente que alguém desreferenciado um null.Ao ver IAE, você assume que o chamador do método no topo da pilha passou 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 de exceção padrã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 capture NPEs 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 deveria fazer para 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 é que uma ilegalidade null é tão especial que merece uma exceção separada de todos os outros tipos de argumentos ilegais?

Finalmente, aceito o argumento dado por outras respostas de que partes da API Java usam NPE dessa maneira.No entanto, a API Java é inconsistente com tudo, desde tipos de exceção até convenções de nomenclatura, então acho que apenas copiar cegamente (sua parte favorita) da API Java não é um argumento bom o suficiente para superar essas outras considerações.

O padrão é jogar o NullPointerException.O geralmente infalível "Java Eficaz" 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":

"Indiscutivelmente, todas as invocações de métodos errôneas se resumem a um argumento ilegal ou estado ilegal, mas outras exceções são usadas padrão para certos tipos de argumentos e estados ilegais.Se um chamador passar nulo em algum parâmetro para o qual os valores nulos são proibidos, a convenção determina que o NullPointerException seja jogado em vez de ilegalargumentException ".

Eu era totalmente a favor de jogar IllegalArgumentException para parâmetros nulos, até hoje, quando notei o java.util.Objects.requireNonNull método em Java 7.Com esse método, em vez de fazer:

if (param == null) {
    throw new IllegalArgumentException("param cannot be null.");
}

você pode fazer:

Objects.requireNonNull(param);

e vai lançar um NullPointerException se o parâmetro que você passa é null.

Dado que esse método está bem no meio de java.util Considero sua existência uma indicação bastante forte de que jogar NullPointerException é "a maneira Java de fazer as coisas".

Acho que estou decidido de qualquer maneira.

Observe que os argumentos sobre depuração difícil são falsos porque você pode, é claro, fornecer uma mensagem para NullPointerException dizendo o que era nulo e por que não deveria ser nulo.Assim como com IllegalArgumentException.

Uma vantagem adicional de NullPointerException é que, em código crítico de alto desempenho, você pode dispensar uma verificação explícita de nulo (e um NullPointerException com uma mensagem de erro amigável) e confie apenas no NullPointerException você obterá automaticamente ao chamar um método no parâmetro nulo.Desde que você chame um método rapidamente (ou seja,falhar rapidamente), então 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 lançar uma mensagem útil para indicar qual parâmetro era nulo, mas é bom ter a opção de alterar isso se o desempenho exigir, sem quebrar o contrato publicado do método/construtor.

Costumo seguir o design de bibliotecas JDK, especialmente Coleções e Simultaneidade (Joshua Bloch, Doug Lea, esses caras sabem como projetar APIs sólidas).De qualquer forma, muitas APIs no JDK lançam proativamente NullPointerException.

Por exemplo, o Javadoc para Map.containsKey afirma:

@Throws NullPointerException Se a chave for nula e este mapa não permite teclas nulas (opcionais).

É perfeitamente válido lançar seu próprio NPE.A convenção é incluir o nome do parâmetro que era nulo na mensagem da exceção.

O padrão é:

public void someMethod(Object mustNotBeNull) {  
    if (mustNotBeNull == null) {  
        throw new NullPointerException("mustNotBeNull must not be null");  
    }  
}

Faça o que fizer, não permita que um valor incorreto 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".

Votei no argumento de Jason Cohen porque foi bem apresentado.Deixe-me desmembrá-lo passo a passo.;-)

  • O JavaDoc NPE diz explicitamente, "outros usos ilegais do objeto nulo".Se fosse limitado apenas a situações em que o tempo de execução encontra um valor nulo quando não deveria, todos esses casos poderiam ser definidos de forma muito mais sucinta.

  • Não posso evitar se você assumir a coisa errada, mas supondo que o encapsulamento seja aplicado corretamente, você realmente não deveria se importar ou notar se um nulo foi desreferenciado de forma inadequada vs.se um método detectou um nulo inadequado e disparou uma exceção.

  • eu escolheria NPE sobre IAE por vários motivos

    • É mais específico sobre a natureza da operação ilegal
    • A lógica que permite nulos por engano tende a ser muito diferente da lógica que permite por engano valores ilegais.Por exemplo, se estou validando dados inseridos por um usuário, se obtiver um valor inaceitável, a origem desse erro está no usuário final da aplicação.Se eu obtiver um valor nulo, será um erro do programador.
    • Valores inválidos podem causar estouros de pilha, erros de falta de memória, exceções de análise, etc.Na verdade, a maioria dos erros geralmente se apresenta, em algum momento, como um valor inválido em alguma chamada de método.Por esta razão, vejo o IAE como, na verdade, o MAIS GERAL de todas as exceções em RuntimeException.
  • Na verdade, outros argumentos inválidos podem resultar em todos os tipos de exceções. UnknownHostException, FileNotFoundException, uma variedade de exceções de erro de sintaxe, IndexOutOfBoundsException, falhas de autenticação, etc., etc.

Em geral, sinto que o NPE é muito difamado porque tradicionalmente tem sido associado a códigos que não seguem as princípio de falha rápida.Isso, somado ao fracasso do JDK em preencher os NPEs com uma sequência de mensagens, realmente criou um forte sentimento negativo que não é bem fundamentado.Na verdade, a diferença entre NPE e IAE do ponto de vista do tempo de execução é estritamente o nome.Dessa perspectiva, quanto mais preciso você for com o nome, mais clareza você dará ao chamador.

É uma questão do estilo “Guerra Santa”.Por outras palavras, ambas as alternativas são boas, mas as pessoas terão as suas preferências que defenderão até à morte.

Se for um setter método e null está sendo passado para ele, acho que faria mais sentido lançar um IllegalArgumentException.A NullPointerException parece fazer mais sentido no caso em que você está tentando realmente usar o null.

Então, se você estiver usando e for null, NullPointer.Se estiver sendo passado e for null, IllegalArgument.

Apache Commons Lang tem um NullArgumentException isso faz uma série de coisas discutidas aqui:ele estende IllegalArgumentException e seu único construtor leva o nome do argumento que deveria ser não nulo.

Embora eu ache que lançar algo como NullArgumentException ou IllegalArgumentException descreve com mais precisão as circunstâncias excepcionais, meus colegas e eu optamos por seguir o conselho de Bloch sobre o assunto.

Não poderia concordar mais com o que está sendo dito.Falhe cedo, falhe rápido.Muito bom mantra de exceção.

A questão 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 estava com um argumento que 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

A prática aceita é usar o IllegalArgumentException(String mensagem) declarar um parâmetro inválido e fornecer o máximo de detalhes possível...Então, para dizer que um parâmetro foi considerado nulo enquanto a exceção não é nula, você faria algo assim:

if( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");

Você praticamente não tem motivo para usar implicitamente o "NullPointerException".O NullPointerException é uma exceção lançada pela Java Virtual Machine quando você tenta executar código em referência nula (como para sequenciar()).

Na verdade, a questão de lançar IllegalArgumentException ou NullPointerException é, na minha humilde opinião, apenas uma "guerra santa" para uma minoria com um entendimento incompleto do tratamento de exceções em Java.Em geral, as regras são simples e são as seguintes:

  • violações de restrições de argumentos devem ser indicadas o mais rápido possível (-> falha rápida), para evitar estados ilegais que são muito mais difíceis de depurar
  • no caso de um ponteiro nulo inválido por qualquer motivo, lance NullPointerException
  • no caso de um índice de array/coleção ilegal, lance ArrayIndexOutOfBounds
  • no caso de um tamanho de array/coleção negativo, lance NegativeArraySizeException
  • no caso de um argumento ilegal que não seja coberto pelo acima e para o qual você não tenha outro tipo de exceção mais específico, lance IllegalArgumentException como uma lixeira
  • por outro lado, no caso de uma violação de restrição DENTRO DE UM CAMPO que não poderia ser evitada por falha rápida por algum motivo válido, capture e lance novamente como IllegalStateException ou uma exceção verificada mais específica.Nunca deixe passar o NullPointerException original, ArrayIndexOutOfBounds, etc neste caso!

Existem pelo menos três boas razões 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 ruim da prática:

(1) Um programador não pode assumir com segurança que todos os casos de violações de restrições de argumentos resultam em IllegalArgumentException, porque a grande maioria das classes padrão usa esta exceção como uma lixeira 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 ao usar suas classes, já que as bibliotecas padrão seguem principalmente regras diferentes que violam as suas, e a maioria dos usuários de sua API também as usará!

(2) Mapear as exceções na verdade 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 verdadeiramente uma NullPointerException e uma IllegalArgumentException, já que 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 na verdade cria o perigo de mascaramento de bugs:Para mapear violações de restrição de argumento em IllegalArgumentException, você precisará codificar um try-catch externo dentro de cada método que tenha argumentos restritos.No entanto, simplesmente capturar RuntimeException neste bloco catch está fora de questão, porque isso corre o risco de mapear RuntimeExceptions documentadas lançadas por métodos libery usados ​​dentro do seu em IllegalArgumentException, mesmo que não sejam causadas 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.Mesmo o mapeamento mais cuidadoso corre o risco de mascarar erros de programação de outros criadores de bibliotecas como violações de restrições de argumentos dos usuários do seu método, o que é simplesmente um comportamento absurdo!

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 chamador 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 por um valor ilegal, repita a chamada com um padrão (para essas exceções específicas é necessário) ou corrija seu código - se, por outro lado Não está documentado para acontecer para um determinado conjunto de argumentos, registre um relatório de bug aos fabricantes do método para garantir que seu código ou sua documentação sejam corrigidos.

Em geral, um desenvolvedor deve nunca lança 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 fazer com que um valor nulo gere uma NullPointerException, você deve lançar uma IllegalArgumentException.

Lançando uma exceção exclusiva para null argumentos (seja NullPointerException ou um tipo personalizado) torna automatizado null testes mais confiáveis.Este teste automatizado pode ser feito com reflexão e um conjunto de valores padrão, como em Goiabade NullPointerTester.Por exemplo, NullPointerTester tentaria chamar o seguinte método ...

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}

...com duas listas de argumentos: "", null e null, ImmutableList.of().Isso testaria se cada uma dessas chamadas gera o esperado NullPointerException.Para esta implementação, passando um null lista faz não produzir NullPointerException.Acontece, no entanto, que produz uma IllegalArgumentException porque NullPointerTester acontece de usar uma string padrão de "".Se NullPointerTester espera apenas NullPointerException para null valores, ele detecta o bug.Se espera IllegalArgumentException, ele sente falta.

Como questão subjetiva, isso deveria ser encerrado, mas como ainda está aberto:

Isto faz parte da política interna utilizada no meu local de trabalho anterior e funcionou muito bem.Isso tudo é de memória, então não consigo lembrar o texto exato.É importante notar que eles não usaram exceções verificadas, mas isso está além do escopo da questão.As exceções não verificadas que usaram caíram em três categorias principais.

Null Pointer Exception:Não jogue intencionalmente.Os NPEs devem ser lançados somente pela VM ao desreferenciar uma referência nula.Todos os esforços possíveis devem ser feitos para garantir que estes nunca sejam lançados.@Nullable e @NotNull devem ser usados ​​em conjunto com ferramentas de análise de código para encontrar esses erros.

Exceção de argumento ilegal: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 enquadraria nesta categoria.

IlegalStateException:Lançado 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 do IndexOutOfBoundsException usadas em coisas que tinham 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 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 existe essa distinção:"Dependendo do tipo de erro, é bastante simples descobrir o que fazer.Mesmo que você não consiga descobrir o que deu errado, você pode descobrir onde detectar esse erro e criar informações adicionais de depuração."

Null Pointer Exception:Trate o caso Nulo ou coloque 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.

Exceção de argumento ilegal:você tem algo errado no seu site de chamada.Se os valores passados ​​forem de outra função, descubra por que você está recebendo um valor incorreto.Se você estiver passando um de seus argumentos, propagar o erro verifica a pilha de chamadas até encontrar a função que não está retornando o que você espera.

IlegalStateException:Você não chamou suas funções na ordem correta.Se você estiver usando um de seus argumentos, verifique-os e lance uma IllegalArgumentException descrevendo o problema.Você pode então propagar as bochechas contra a pilha até encontrar o problema.

De qualquer forma, o que ele queria dizer era que você só pode copiar IllegalArgumentAssertions para cima na pilha.Não há como propagar IllegalStateExceptions ou NullPointerExceptions na pilha porque eles têm algo a ver com sua função.

Eu queria destacar os argumentos nulos de outros argumentos ilegais, então criei uma exceção do IAE chamada NullArgumentException.Sem nem precisar ler a mensagem de exceção, sei que um argumento nulo foi passado para um método e ao ler a mensagem descubro qual argumento era nulo.Ainda capturo NullArgumentException com um manipulador IAE, mas é em meus logs que posso ver a diferença rapidamente.

a dicotomia...Eles não se sobrepõem?Somente partes não sobrepostas de um todo podem constituir uma dicotomia.Da maneira que eu vejo:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

Algumas coleções assumem que null é rejeitado usando NullPointerException em vez de IllegalArgumentException.Por exemplo, se você comparar um conjunto contendo null para um conjunto que rejeita null, o primeiro set chamará containsAll do outro e pegar seu NullPointerException -- mas não IllegalArgumentException.(Estou olhando para a implementação de AbstractSet.equals.)

Você poderia razoavelmente argumentar que usar exceções não verificadas dessa forma é um antipadrão, que comparar coleções que contêm null para coleções que não podem conter null é um bug provável que realmente deve produzir uma exceção, ou que colocar null em uma coleção é uma má ideia.No entanto, a menos que você esteja disposto a dizer isso equals deveria lançar uma exceção nesse caso, você ficará preso lembrando que NullPointerException é necessária em certas circunstâncias, mas não em outras.("IAE antes de NPE exceto depois de 'c'...")

NullPointerException lançada ao tentar acessar um objeto com uma variável de referência cujo valor atual é nulo

IllegalArgumentException lançada quando um método recebe um argumento formatado de forma diferente do que o método espera

De acordo com o seu cenário, IllegalArgumentException é a melhor escolha, porque null não é um valor válido para sua propriedade.

Idealmente, exceções de tempo de execução não devem ser lançadas.Uma exceção verificada (exceção comercial) deve ser criada para o seu cenário.Porque se qualquer uma dessas exceções for lançada e registrada, ela desorientará o desenvolvedor ao analisar os 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 de logs.

As definições dos links para as duas exceções acima são ilegalArgumentException:Lançado para indicar que um método recebeu um argumento ilegal ou inapropriado.Null Pointer Exception:Lançado quando um aplicativo tenta usar null em um caso em que um objeto é necessário.

A grande diferença aqui é que IllegalArgumentException deve ser usado ao verificar se um argumento para um método é válido.NullPointerException deve ser usado sempre que um objeto estiver sendo "usado" quando for nulo.

Espero que isso ajude a colocar os dois em perspectiva.

Se for um "setter" ou em algum lugar que estou conseguindo que um membro use mais tarde, costumo usar IllegalArgumentException.

Se for algo que vou usar (desreferência) agora no método, lanço uma NullPointerException proativamente.Eu gosto mais disso do que deixar o tempo de execução fazer isso, porque posso fornecer uma mensagem útil (parece que o tempo de execução também poderia fazer isso, mas isso é um discurso retórico para outro dia).

Se estou substituindo um método, uso o que o método substituído usa.

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 NPE lançado pela VM, que qualquer programador não perceberia imediatamente seu erro e começaria a procurar aleatoriamente, ou pior, culparia seu código por ter 'bugs'.

Nesse caso, IllegalArgumentException transmite informações claras ao usuário usando sua API de que "não deve ser nulo".Como outros usuários do fórum apontaram, você pode usar o NPE se quiser, desde que transmita as informações corretas ao usuário usando sua API.

GaryF e tweakt abandonaram as referências "Effective Java" (que eu juro) que recomendam o uso de NPE.E observar como outras APIs boas são construídas é a melhor maneira de ver como construir sua API.

Outro bom exemplo é observar as APIs do Spring.Por exemplo, org.springframework.beans.BeanUtils.instantiateClass(Constructor ctor, Object[] args) tem uma linha Assert.notNull(ctor, "Constructor must not be null").O método org.springframework.util.Assert.notNull(Object object, String message) verifica se o argumento (objeto) passado é nulo e se for, lança uma nova IllegalArgumentException(message) que é então capturada na organização. método springframework.beans.BeanUtils.instantiateClass(...).

Se você optar por lançar um NPE e estiver usando o argumento em seu método, pode ser redundante e caro verificar explicitamente um nulo.Acho que a VM já faz isso por você.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top