Deve-se procurar nulo se ele não espera nulo?


113

Na semana passada, tivemos uma discussão acalorada sobre como lidar com nulos na camada de serviço de nosso aplicativo. A questão está no contexto do .NET, mas será a mesma em Java e em muitas outras tecnologias.

A pergunta era: você deve sempre verificar nulos e fazer com que seu código funcione, não importa o quê, ou deixar uma exceção surgir quando um nulo é recebido inesperadamente?

Por um lado, verificar nulo onde você não o espera (ou seja, não possui interface com o usuário para lidar com isso) é, na minha opinião, o mesmo que escrever um bloco try com catch vazio. Você está apenas ocultando um erro. O erro pode ser que algo mudou no código e nulo agora é um valor esperado, ou há algum outro erro e o ID errado é passado para o método.

Por outro lado, verificar nulos pode ser um bom hábito em geral. Além disso, se houver uma verificação, o aplicativo poderá continuar funcionando, com apenas uma pequena parte da funcionalidade sem efeito. Em seguida, o cliente pode relatar um pequeno bug como "não é possível excluir o comentário" em vez de um bug muito mais grave como "não é possível abrir a página X".

Que prática você segue e quais são seus argumentos a favor ou contra qualquer uma dessas abordagens?

Atualizar:

Quero acrescentar alguns detalhes sobre o nosso caso em particular. Estávamos recuperando alguns objetos do banco de dados e fizemos algum processamento neles (digamos, construímos uma coleção). O desenvolvedor que escreveu o código não previu que o objeto pudesse ser nulo, portanto não incluiu nenhuma verificação e, quando a página foi carregada, ocorreu um erro e a página inteira não foi carregada.

Obviamente, nesse caso, deveria ter havido um cheque. Em seguida, discutimos se todos os objetos processados ​​devem ser verificados, mesmo que não se espere que eles estejam ausentes, e se o processamento final deve ser interrompido silenciosamente.

O benefício hipotético seria que a página continuasse funcionando. Pense nos resultados de uma pesquisa no Stack Exchange em diferentes grupos (usuários, comentários, perguntas). O método pode verificar se há nulo e abortar o processamento de usuários (que devido a um erro é nulo), mas retornar as seções "comentários" e "perguntas". A página continuaria funcionando, exceto que a seção "usuários" estará ausente (o que é um bug). Devemos falhar cedo e quebrar a página inteira ou continuar a trabalhar e esperar que alguém note que a seção "usuários" está ausente?


62
Princípio Fox Mulder: Não confie em ninguém.
Yannis

14
@YannisRizos: esse princípio é bom - é tão bom que os criadores de Java e C # permitem que o ambiente de execução de linguagem faça isso automaticamente. Portanto, normalmente não é necessário fazer verificações explícitas para nulo em qualquer lugar do seu código nesses idiomas.
Doc Brown


Eu concordo com a resposta do Tdammers. Acredito que verificar nulo está errado, porque você não tem uma maneira razoável de lidar com isso. No entanto, no seu cenário, você pode lidar com uma seção com falha, como a validação de um usuário (ou a obtenção de comentários ou o que for) e com a caixa "oh, não ocorreu um erro". Eu, pessoalmente, registrar todas as exceções em meu aplicativo e filtrar certas exceções, como 404s e estreita ligação inesperadamente

2
assert(foo != null, "foo is web control within the repeater, there's no reason to expect it to be null, etc, etc...");
ZzzzBov

Respostas:


105

A questão não é tanto se você deve procurar nullou deixar o tempo de execução lançar uma exceção; é assim que você deve responder a uma situação tão inesperada.

Suas opções, então, são:

  • Lance uma exceção genérica ( NullReferenceException) e deixe borbulhar; se você não fizer a nullverificação, é isso que acontece automaticamente.
  • Lance uma exceção personalizada que descreve o problema em um nível superior; isso pode ser alcançado lançando a nullverificação ou capturando NullReferenceExceptione lançando a exceção mais específica.
  • Recupere-se substituindo um valor padrão adequado.

Não existe uma regra geral sobre qual é a melhor solução. Pessoalmente, eu diria:

  • A exceção genérica é melhor se a condição de erro for um sinal de um bug sério no seu código, ou seja, o valor nulo nunca deve ser permitido chegar lá em primeiro lugar. Essa exceção pode entrar em qualquer ponto do registro de erros que você configurou, para que alguém seja notificado e corrija o erro.
  • A solução de valor padrão é boa se o fornecimento de saída semi-útil for mais importante que a correção, como um navegador da Web que aceite HTML tecnicamente incorreto e faça o possível para torná-lo sensato.
  • Caso contrário, eu iria com a exceção específica e a trataria em algum lugar adequado.

1
@Tilgar para o usuário final, não há diferença. para o developper uma exceção específica como "banco de dados do servidor não está acessível" é mais intuitivo que "excepteion ponteiro nulo"
k3b

32
Falhando no início e no início significa que há menos risco de coisas darem sutilmente errado na linha, coisas como autenticação incorreta, dados corrompidos persistentes para armazenamento, vazamento de informações e outras coisas divertidas.
Lars Viklund

1
@Tilgar: Existem duas preocupações aqui: comportamento autônomo e manutenção. Embora, para o comportamento autônomo, haja pouca diferença entre uma exceção específica e uma genérica, a manutenção pode fazer a diferença entre "ah, é por isso que as coisas explodem" e um depuração de várias horas.
Tdammers # 7/12

3
Tdammers está exatamente certo. A "referência de objeto não definida para uma instância de um objeto" é uma das exceções mais irritantes que vejo com mais frequência. Eu preferiria ver uma exceção específica, seja uma ArgumentNullException com o nome do parâmetro ou uma exceção personalizada "Nenhum registro de postagem existe para o usuário TK421".
Sean Chase

1
@ k3b Também para o utilizador final, "o servidor de base de dados não está acessível" é muito útil. Pelo menos, para qualquer usuário final que tenha alguma pista sobre problemas comuns - pode ajudá-lo a descobrir que um cabo está solto, o roteador precisa ser reiniciado etc., em vez de ficar com raiva do software de buggy.
perfil completo de Julia Hayward

58

IMHO tentar lidar com valores nulos que você não espera leva a códigos excessivamente complicados. Se você não espera nulo, deixe claro jogando ArgumentNullException. Fico realmente frustrado quando as pessoas verificam se o valor é nulo e, em seguida, tentam escrever um código que não faz sentido. O mesmo se aplica ao uso SingleOrDefault(ou pior ainda, da coleta) quando alguém realmente espera Singlee em muitos outros casos em que as pessoas têm medo (eu realmente não sei o que) para afirmar claramente sua lógica.


Em vez de ArgumentNullException, deve-se usar contratos de código (a menos que seja um código-fonte anterior a 2010 que não suporta contratos de código).
Arseni Mourzenko

Eu concordo com você. Se o nulo não for esperado, ele não deve ser tratado de maneiras estranhas, mas apenas relatado.
Giorgio

9
se eu li a pergunta corretamente, lançando um ArgumentNullExceptionconta como "manipulando" o valor nulo: evita uma exceção de referência nula posterior.
KutuluMike

@ MichaelEdenfield: Eu não acho que isso conte como manipulação real de acordo com a pergunta dada (nosso objetivo não é fazer com que seu código funcione, não importa o que aconteça ). Para ser sincero, geralmente esqueço de escrever o bloco apropriado que será lançado se o nulo for passado. No entanto, considero que jogar NullArgumentException é a melhor prática e, na minha opinião, escrever código que não pode manipular nulos, mas em vez de lançar retornos de exceção, por exemplo, outro nulo como resultado do método (ou sequência vazia, coleção vazia ou -1 , ou pula a execução do método se o tipo de retorno for nulo, etc.).
Empi #

2
@Giorgio, ArgumentNullExceptiondeixa muito claro qual é o problema e como corrigi-lo. C # não costuma usar "indefinido". Se você chamar algo de uma maneira indefinida, a maneira como você está chamando não faz sentido. Uma exceção é a maneira correta de indicar isso. Agora, às vezes, tornarei métodos de análise que retornam nulos se o int(por exemplo) não puder ser analisado. Mas esse é um caso em que um resultado incomparável é uma possibilidade legítima e não um erro de uso. Veja também este artigo .
Kyralessa

35

O hábito de nullprocurar a minha experiência vem de ex-desenvolvedores de C ou C ++; nesses idiomas, você tem uma boa chance de ocultar um erro grave ao não procurar NULL. Como você sabe, em Java ou C # as coisas são diferentes, o uso de uma ref nula sem verificação nullcausará uma exceção; portanto, o erro não será oculto secretamente desde que você não o ignore no lado da chamada. Portanto, na maioria dos casos, procurar explicitamente nullnão faz muito sentido e complica demais o código mais do que o necessário. É por isso que não considero a verificação nula um bom hábito nesses idiomas (pelo menos, não em geral).

Claro, existem exceções a essa regra, eis as que eu consigo pensar:

  • você deseja uma mensagem de erro legível por humanos, informando ao usuário em que parte do programa ocorreu a referência nula incorreta. Portanto, seu teste para null lança apenas uma exceção diferente, com um texto de erro diferente. Obviamente, nenhum erro será mascarado dessa maneira.

  • você quer que seu programa "falhe mais cedo". Por exemplo, você deseja verificar um valor nulo de um parâmetro construtor, onde a referência do objeto seria armazenada apenas em uma variável de membro e usada posteriormente.

  • você pode lidar com a situação de uma ref nula com segurança, sem o risco de falhas subseqüentes e sem mascarar um erro grave.

  • você deseja um tipo completamente diferente de sinalização de erro (por exemplo, retornando um código de erro) no seu contexto atual


3
"você deseja uma mensagem de erro legível por humanos" - esse é o melhor motivo para fazê-lo, na minha opinião. "Referência de objeto não definida para uma instância de um objeto" ou "Exceção de referência nula" nunca é tão útil quanto "Produto não instanciado".
Pd

@pdr: Outro motivo para verificar é garantir que ele seja sempre detectado.
Giorgio

13
"Antigos desenvolvedores C", talvez. "Antigos desenvolvedores de C ++" apenas se eles próprios estiverem recuperando desenvolvedores de C. Como um desenvolvedor moderno de C ++, eu suspeitaria muito do código que usa ponteiros nus ou alocação dinâmica imprudente. Na verdade, eu ficaria tentado a mudar o argumento e dizer que o C ++ não sofre o problema que o OP descreve porque suas variáveis ​​não têm a propriedade adicional de nulidade especial que Java e C # possuem.
Kerrek SB

7
Seu primeiro parágrafo é apenas metade da história: sim, se você usar uma referência nula em C #, receberá uma exceção, enquanto que em C ++ obterá um comportamento indefinido. Mas em C # bem escrito, você está preso a referências muito mais anuláveis ​​do que em C ++ bem escrito.
James McNellis

Quanto melhor o encapsulamento, melhor esta resposta se aplica.
Radarbob 11/10/2015

15

Use asserts para testar e indicar pré / pós-condições e invariantes para seu código.

Torna muito mais fácil entender o que o código espera e com o que se espera que ele lide.

Uma declaração, IMO, é uma verificação adequada porque é:

  • eficiente (geralmente não usado na liberação),
  • breve (geralmente uma única linha) e
  • claro (pré-condição violada, este é um erro de programação).

Eu acho que o código de auto-documentação é importante, portanto, verificar é bom. A programação defensiva e à prova de falhas facilita a manutenção, que é onde geralmente passa mais tempo.

Atualizar

Escreveu sua elaboração. Geralmente, é bom oferecer ao usuário final um aplicativo que funcione bem com erros, ou seja, mostre o máximo possível. A robustez é boa, porque o céu sabe apenas o que quebrará em um momento crítico.

No entanto, os desenvolvedores e testadores devem estar cientes dos erros o mais rápido possível, portanto, você provavelmente deseja uma estrutura de log com um gancho que possa exibir um alerta ao usuário de que houve um problema. O alerta provavelmente deve ser mostrado a todos os usuários, mas provavelmente pode ser adaptado de maneira diferente, dependendo do ambiente de tempo de execução.


afirma que não estar presente no release é um grande ponto negativo para mim, o release é o horário exato em que você deseja obter as informações adicionais
jk.

1
Bem, sinta-se à vontade, use uma função afirmativa de ativação que também está ativa no lançamento, se você precisar. Eu rolei o meu com bastante frequência.
Macke

7

Falhe cedo, falhe frequentemente. nullé um dos melhores valores inesperados que você pode obter, pois pode falhar rapidamente assim que tentar usá-lo. Outros valores inesperados não são tão fáceis de detectar. Como nullfalha automaticamente para você sempre que você tenta usá-lo, eu diria que ele não requer uma verificação explícita.


28
Eu espero que você quis dizer: Falha cedo, falhar rapidamente, muitas vezes não é ...
empi

12
O problema é que, sem verificações explícitas, um valor nulo às vezes pode se propagar bastante antes de causar uma exceção e, em seguida, pode ser muito difícil descobrir de onde se originou.
Michael Borgwardt

@MichaelBorgwardt Não é para isso que servem os rastreamentos de pilha?
R0MANARMY

6
@ R0MANARMY: os rastreamentos de pilha não ajudam quando um valor nulo é salvo em algum campo e a NullPointerException é lançada muito mais tarde.
Michael Borgwardt

@ R0MANARMY, mesmo que você pudesse confiar no número da linha no rastreamento da pilha (em muitos casos, você não pode) algumas linhas contêm várias variáveis ​​que podem ser nulas.
Ohad Schneider

6
  • A verificação de um nulo deve ser sua segunda natureza

  • Sua API será usada por outras pessoas e é provável que a expectativa delas seja diferente da sua

  • Testes de unidade detalhados normalmente destacam a necessidade de uma verificação de referência nula

  • A verificação de nulos não implica instruções condicionais. Se você se preocupa que a verificação nula torne seu código menos legível, considere os contratos de código .NET.

  • Considere instalar o ReSharper (ou similar) para pesquisar seu código por verificações de referência nula ausentes


O uso de contratos de código para pré-condições é simplesmente declarações condicionais glorificadas.
um CVn

Sim, eu concordo, mas também acho que o código parece mais limpo ao usar contratos de código, ou seja, sua legibilidade é aprimorada.
CodeART

4

Eu consideraria a questão do ponto de vista da semântica, ou seja, me perguntaria o que um ponteiro NULL ou um argumento de referência nulo representa.

Se uma função ou método foo () possui um argumento x do tipo T, x pode ser obrigatório ou opcional .

Em C ++, você pode passar um argumento obrigatório como

  • Um valor, por exemplo, void foo (T x)
  • Uma referência, por exemplo, void foo (T & x).

Nos dois casos, você não tem o problema de verificar um ponteiro NULL. Assim, em muitos casos, você não precisa de uma verificação de ponteiro nulo em tudo para argumentos obrigatórios.

Se você estiver passando um argumento opcional , poderá usar um ponteiro e usar o valor NULL para indicar que nenhum valor foi fornecido:

void foo(T* x)

Uma alternativa é usar um ponteiro inteligente como

void foo(shared_ptr<T> x)

Nesse caso, você provavelmente deseja verificar o ponteiro no código, porque faz diferença para o método foo () se x contém um valor ou nenhum valor.

O terceiro e último caso é que você usa um ponteiro para um argumento obrigatório . Nesse caso, chamar foo (x) com x == NULL é um erro e você deve decidir como lidar com isso (o mesmo que com um índice fora dos limites ou qualquer entrada inválida):

  1. Sem verificação: se houver um erro, deixe-o travar e espere que ele apareça cedo o suficiente (durante o teste). Se isso não acontecer (se você falhar cedo o suficiente), espere que ele não apareça em um sistema de produção, porque a experiência do usuário é que eles veem a falha do aplicativo.
  2. Verifique todos os argumentos no início do método e relate argumentos NULL inválidos, por exemplo, lance uma exceção de foo () e deixe outro método lidar com isso. Provavelmente, a melhor coisa a fazer é mostrar ao usuário uma janela de diálogo informando que o aplicativo encontrou um erro interno e será fechado.

Além de uma maneira mais agradável de falhar (fornecendo mais informações ao usuário), uma vantagem da segunda abordagem é que é mais provável que o bug seja encontrado: o programa falhará toda vez que o método for chamado com argumentos errados, enquanto que com Abordagem 1, o bug só aparecerá se uma instrução que desreferenciar o ponteiro for executada (por exemplo, se o ramo direito de uma instrução if for inserido). Portanto, a abordagem 2 oferece uma forma mais forte de "falhar cedo".

Em Java, você tem menos opções porque todos os objetos são transmitidos usando referências que sempre podem ser nulas. Novamente, se o nulo representa um valor NENHUM de um argumento opcional, a verificação do nulo (provavelmente) fará parte da lógica de implementação.

Se o nulo é uma entrada inválida, você tem um erro e eu aplicaria considerações semelhantes às do ponteiro de C ++. Como em Java você pode capturar exceções de ponteiro nulo, a abordagem 1 acima é equivalente à abordagem 2 se o método foo () derereferenciar todos os argumentos de entrada em todos os caminhos de execução (o que provavelmente ocorre com frequência em métodos pequenos).

Resumindo

  • Tanto em C ++ quanto em Java, verificar se os argumentos opcionais são nulos faz parte da lógica do programa.
  • No C ++, eu sempre verificava os argumentos obrigatórios para garantir que uma exceção adequada fosse lançada para que o programa pudesse lidar com isso de uma maneira robusta.
  • Em Java (ou C #), eu verificaria os argumentos obrigatórios se houver caminhos de execução que não serão lançados para ter uma forma mais forte de "falha antecipada".

EDITAR

Obrigado a Stilgar para mais detalhes.

No seu caso, parece que você tem nulo como resultado de um método. Novamente, acho que você deve primeiro esclarecer (e corrigir) a semântica de seus métodos antes de tomar uma decisão.

Portanto, você tem o método m1 () chamando o método m2 () e m2 () retorna uma referência (em Java) ou um ponteiro (em C ++) para algum objeto.

Qual é a semântica de m2 ()? M2 () sempre deve retornar um resultado não nulo? Se for esse o caso, um resultado nulo é um erro interno (em m2 ()). Se você verificar o valor de retorno em m1 (), poderá ter um tratamento de erro mais robusto. Caso contrário, você provavelmente terá uma exceção, mais cedo ou mais tarde. Talvez não. Espero que a exceção seja lançada durante o teste e não após a implantação. Pergunta : se m2 () nunca deve retornar nulo, por que está retornando nulo? Talvez devesse lançar uma exceção? m2 () provavelmente é de buggy.

A alternativa é que retornar null faz parte da semântica do método m2 (), ou seja, possui um resultado opcional, que deve ser documentado na documentação do método Javadoc. Nesse caso, não há problema em ter um valor nulo. O método m1 () deve verificar como qualquer outro valor: não há erro aqui, mas provavelmente verificar se o resultado é nulo é apenas parte da lógica do programa.


Caso contrário, não foi o argumento que foi nulo. Fizemos uma chamada ao banco de dados para algo que se esperava que estivesse presente, mas na prática ele não estava presente. A questão é se devemos verificar se todos os objetos existem ou devemos verificar apenas se esperamos que esteja faltando.
Stilgar

Portanto, o resultado retornado por um método foi uma referência a um objeto e null foi o valor usado para representar o resultado: NOT FOUND. Eu compreendo corretamente?
Giorgio

Atualizei a pergunta com detalhes adicionais.
Stilgar #

3

Minha abordagem geral é nunca testar uma condição de erro que você não sabe como lidar . Então a pergunta que precisa ser respondida em cada instância específica se torna: você pode fazer algo razoável na presença do nullvalor inesperado ?

Se você puder continuar com segurança substituindo outro valor (uma coleção vazia, por exemplo, ou um valor ou instância padrão), faça isso de qualquer maneira. O exemplo de @ Shahbaz do World of Warcraft de substituir uma imagem por outra se enquadra nessa categoria; a imagem em si provavelmente é apenas decoração e não tem impacto funcional . (Em uma compilação de depuração, eu usaria uma cor gritante para chamar a atenção para o fato de que a substituição ocorreu, mas isso depende muito da natureza do aplicativo.) No entanto, registre detalhes sobre o erro, para que você saiba que ele está ocorrendo; caso contrário, você estará ocultando um erro que provavelmente deseja conhecer. Escondê-lo do usuário é uma coisa (novamente, supondo que o processamento possa continuar com segurança); escondê-lo do desenvolvedor é outra completamente diferente.

Se você não puder continuar com segurança na presença de um valor nulo, falhe com um erro sensível; em geral, prefiro falhar o mais rápido possível, quando é óbvio que a operação não pode ser bem-sucedida, o que implica verificar as pré-condições. Há momentos em que você não deseja falhar imediatamente, mas adia a falha pelo maior tempo possível; essa consideração surge, por exemplo, em aplicativos relacionados à criptografia, onde ataques de canal lateral podem divulgar informações que você não deseja que sejam conhecidas. No final, permita que o erro atinja algum manipulador de erro geral, que, por sua vez, registra detalhes relevantes e exibe uma boa (por mais agradável que seja) mensagem de erro para o usuário.

Também é importante notar que a resposta adequada pode muito bem diferir entre testes / controle de qualidade / compilações de depuração e produção / versão. Nas compilações de depuração, eu falharia cedo e muito com mensagens de erro muito detalhadas, para tornar completamente óbvio que há um problema e permitir que um post-mortem determine o que precisa ser feito para corrigi-lo. As construções de produção, quando confrontadas com erros recuperáveis com segurança , provavelmente devem favorecer a recuperação.


Obviamente, existe um tratamento geral de erros na cadeia. O problema com os logs é que talvez não haja ninguém para verificá-los por muito tempo.
Stilgar

@Tilgar, se não houver ninguém para verificar os logs, isso não será um problema com a existência ou a ausência de verificações nulas.
um CVn

2
Há um problema. Os usuários finais podem não perceber que o sistema não funciona corretamente por um longo tempo se houver verificações nulas que simplesmente ocultam o erro e o gravam nos logs.
Stilgar

@Tilgar, isso depende inteiramente da natureza do erro. Como eu disse no post, somente se você puder continuar com segurança se o nulo inesperado for substituído / ignorado. Usar uma imagem em vez de outra em uma versão de compilação (nas versões de depuração, falha o mais cedo e o mais difícil possível para que o problema se torne óbvio) é um animal muito diferente do que retornar resultados inválidos para um cálculo, por exemplo. (Resposta editada para incluir isso.)
um CVn

1
Eu continuaria com falhas rapidamente e no início da produção, a menos que você possa, de alguma forma, registrar e relatar o erro (sem depender muito de usuários experientes). Ocultar seus erros implantados dos usuários é uma coisa - escondê-los de si mesmo é perigoso. Permitir também que o usuário viva com erros sutis pode desgastar sua confiança em seu aplicativo. Às vezes, é melhor morder a bala e informar que houve um erro e que você a corrigiu rapidamente.
Merlyn Morgan-Graham

2

Claro que NULLnão é esperado , mas sempre há bugs!

Acredito que você deve verificar NULLse não espera isso. Deixe-me dar exemplos (embora eles não estejam em Java).

  • No World of Warcraft, devido a um bug, a imagem de um dos desenvolvedores apareceu em um cubo para muitos objetos. Imagine se o mecanismo gráfico não esperasse NULL indicadores, teria ocorrido um acidente, enquanto foi transformado em uma experiência não tão fatal (até engraçada) para o usuário. O mínimo é que você não teria perdido inesperadamente sua conexão ou qualquer um dos seus pontos de salvamento.

    Este é um exemplo em que a verificação de NULL impediu uma falha e o caso foi tratado silenciosamente.

  • Imagine um programa de caixa bancário. A interface faz algumas verificações e envia dados de entrada para uma parte subjacente para executar as transferências de dinheiro. Se a parte principal espera não receber NULL, um bug na interface pode causar um problema na transação, possivelmente perdendo algum dinheiro no meio (ou pior, duplicado).

    Por "um problema", quero dizer um travamento (como no travamento de travamento (digamos que o programa seja escrito em C ++), não uma exceção de ponteiro nulo). Nesse caso, a transação precisa reverter se NULL for encontrado (o que obviamente não seria possível se você não verificasse as variáveis ​​em relação a NULL!)

Tenho certeza que você entendeu.

A verificação da validade dos argumentos para suas funções não atrapalha seu código, pois há algumas verificações na parte superior da sua função (não distribuídas no meio) e aumenta muito a robustez.

Se você tiver que escolher entre "o programa informa ao usuário que ele falhou e normalmente é encerrado ou volta a um estado consistente, possivelmente criando um log de erros" e "Violação de acesso", você não escolheria o primeiro? Isso significa que todas as funções devem estar preparadas para serem chamadas por um código de buggy, em vez de esperar que tudo esteja correto, simplesmente porque sempre existem bugs.


Seu segundo exemplo refuta seu argumento. Em uma transação bancária, se algo der errado, a transação deve abortar. É precisamente quando você cobre o erro que o dinheiro pode ser perdido ou duplicado. Verificando null seria "O cliente envia dinheiro nulo ... bem, enviarei apenas US $ 10 (o equivalente à imagem do desenvolvedor)"
Stilgar

@Tilgar, pelo contrário! A verificação de NULL seria cancelada com a mensagem. Não verificar NULL seria uma falha . Agora, uma falha no início da transação pode não ser ruim, mas no meio pode ser catastrófico. (Eu não disse que a transferência bancária deve lidar com o erro como o jogo Apenas o fato de que ele! Devem lidar com isso)
Shahbaz

2
O ponto principal de uma "transação" é que ela não pode ser concluída pela metade :) Se houver um erro, ela será revertida.
Stilgar

Esse é um péssimo conselho para qualquer coisa em que a semântica transacional seja realmente necessária. Você deve fazer com que todas as suas transações sejam atômicas e idempotentes, caso contrário, qualquer erro que ocorrerá garantirá recursos perdidos ou duplicados (dinheiro). Se você precisar lidar com operações não atômicas ou não idempotentes, envolva-o com muito cuidado em camadas que garantam um comportamento atômico e idempotente (mesmo que você mesmo precise escrever essas camadas). Sempre pense: o que acontece se um meteoro atinge o servidor da web no momento em que essa transação está acontecendo?
Merlyn Morgan-Graham

1
@ MerlynMorgan-Graham, eu fiz isso. Espero que isso esclareça a confusão.
Shahbaz

2

Como as outras respostas apontaram Se você não espera NULL, deixe claro jogando ArgumentNullException. Na minha opinião, quando você está depurando o projeto, ele ajuda a descobrir as falhas na lógica do seu programa mais cedo.

Então, você lançará o seu software; se você restaurar essas NullRefrencesverificações, não perderá nada na lógica do seu software, apenas o dobro da certeza de que um bug grave não será exibido.

Outro ponto é que, se a NULLverificação estiver nos parâmetros de uma função, você poderá decidir se a função é o local certo para fazê-lo ou não; caso contrário, encontre todas as referências à função e, em seguida, antes de passar um parâmetro para a função, revise os cenários prováveis ​​que podem levar a uma referência nula.


1

Cada uma nullde seu sistema é uma bomba-relógio esperando para ser descoberta. Verifique nullnos limites onde seu código interage com o código que você não controla e proíba nulldentro do seu próprio código. Isso o livra do problema sem confiar ingenuamente no código estrangeiro. Use exceções ou algum tipo de Maybe/ Nothingtipo.


0

Embora uma exceção de ponteiro nulo seja lançada, muitas vezes ela será lançada longe do ponto em que você obteve o ponteiro nulo. Faça um favor a si mesmo e reduza o tempo que leva para corrigir o bug registrando pelo menos o fato de que você obtém um ponteiro nulo o mais rápido possível.

Mesmo se você não souber o que fazer agora, registrar o evento economizará tempo mais tarde. E talvez o cara que recebe o ingresso consiga usar o tempo economizado para descobrir a resposta adequada.


-1

Como o Fail early, fail fast@DeadMG (@empi) não é pssível, pois o aplicativo Web deve funcionar bem com erros, você deve aplicar a regra

nenhuma exceção capturando sem registrar

para que você, como desenvolvedores, esteja ciente dos possíveis problemas, inspecionando os logs.

se você estiver usando uma estrutura de log como log4net ou log4j, poderá configurar uma saída de log especial adicional (também conhecida como appender) que enviará erros e erros fatais. para que você fique informado .

[atualizar]

se o usuário final da página não estiver ciente do erro, você poderá configurar um appender que envie por e-mail os erros e os erros fatais que permanecerão informados.

se estiver correto que o usuário final da página veja mensagens de erro enigmáticas, você pode configurar um aplicativo que coloca o log de erros para o processamento da página no final da página.

Não importa como você usa o log, se você engolir a exceção, o programa continua com dados que fazem sentido e a deglutição não é mais silenciosa.


1
A questão é o que acontece com a página?
Stilgar

Como sabemos que os dados fazem sentido e o sistema está em um estado consistente. Afinal, o erro foi inesperado, por isso não sabemos qual foi o impacto.
Stilgar

"Como falhar com antecedência, falhe rapidamente, conforme declarado por @DeadMG (@empi), não é pssible porque o aplicativo da web deve funcionar bem com erros, você deve aplicar a regra": sempre é possível capturar todas as exceções (catch (Throwable t)) e redirecionar para alguma página de erro. Isso não seria possível?
Giorgio

-1

Já existem boas respostas para essa pergunta, pode ser uma adição a elas: prefiro afirmações no meu código. Se o seu código puder funcionar apenas com objetos válidos, mas porca com nulo, você deve declarar o valor nulo.

As afirmações nesse tipo de situação permitiriam que qualquer teste de unidade e / ou integração falhasse com a mensagem exata, para que você possa resolver o problema rapidamente antes da produção. Se esse erro passar pelos testes e for para a produção (onde as asserções devem ser desativadas), você receberá NpEx e um bug grave, mas é melhor do que algum bug menor, que é muito mais confuso e aparece em algum outro lugar do código e seria mais caro de corrigir.

Além disso, se alguém precisar trabalhar com suas asserções de código, ele lhe informa sobre suas suposições feitas durante o design / gravação de seu código (neste caso: Ei, cara, esse objeto deve ser apresentado!), O que facilita a manutenção.


Você poderia escrever algo para votar em baixa? Eu apreciaria uma discussão. Thanx
GeT

Não votei e concordo com sua ideia geral. A linguagem precisa de algum trabalho. Suas asserções definem o contrato da sua API e você falha cedo / falha rapidamente se esses contratos forem violados. Fazer isso na capa do seu aplicativo permitirá que os usuários do seu código saibam que eles fizeram algo errado no código. Se eles virem uma pilha rastreada nas reentrâncias do seu código, provavelmente acharão que seu código está com bugs - e você também pode pensar até obter uma reprodução sólida e depurá-la.
Merlyn Morgan-Graham

-1

Normalmente, procuro nulos, mas "manipulo" nulos inesperados lançando uma exceção que fornece um pouco mais de detalhes sobre exatamente onde o nulo ocorreu.

Edit: Se você receber um nulo quando não espera que algo esteja errado - o programa deve morrer.


-1

Depende da implementação do idioma das exceções. As exceções permitem que você execute alguma ação para evitar danos aos dados ou liberar recursos de forma limpa, caso o sistema entre em um estado ou condição imprevisível. Isso é diferente da manipulação de erros, condição que você pode antecipar. Minha filosofia é que você deve ter o mínimo de exceção possível. É barulho. Seu método pode fazer algo razoável ao lidar com a exceção? Ele pode se recuperar? Ele pode reparar ou evitar mais danos? Se você apenas capturar a exceção e retornar do método ou falhar de alguma outra maneira inofensiva, não havia realmente sentido em capturar a exceção. Você acrescentaria apenas ruído e complexidade ao seu método e talvez oculte a fonte de uma falha no seu design. Nesse caso, deixaria a falha borbulhar e seria pega por um loop externo. Se você pode fazer algo como reparar um objeto ou marcá-lo como corrompido ou liberar algum recurso crítico, como um arquivo de bloqueio ou fechar um soquete de maneira limpa, esse é um bom uso de exceções. Se você realmente espera que o NULL seja exibido com frequência como um caso de borda válido, você deve lidar com isso no fluxo lógico normal com instruções if-the-else ou switch-case ou qualquer outra coisa, não com um tratamento de exceção. Por exemplo, um campo desabilitado em um formulário pode ser definido como NULL, que é diferente de uma sequência vazia que representa um campo deixado em branco. Esta é uma área em que você deve usar o bom senso e o bom senso. Eu nunca ouvi falar de boas e sólidas regras práticas sobre como lidar com esse problema em todas as situações. Se você pode fazer algo como reparar um objeto ou marcá-lo como corrompido ou liberar algum recurso crítico, como um arquivo de bloqueio ou fechar um soquete de maneira limpa, esse é um bom uso de exceções. Se você realmente espera que o NULL seja exibido com frequência como um caso de borda válido, você deve lidar com isso no fluxo lógico normal com instruções if-the-else ou switch-case ou qualquer outra coisa, não com um tratamento de exceção. Por exemplo, um campo desabilitado em um formulário pode ser definido como NULL, que é diferente de uma sequência vazia que representa um campo deixado em branco. Esta é uma área em que você deve usar o bom senso e o bom senso. Eu nunca ouvi falar de boas e sólidas regras práticas sobre como lidar com esse problema em todas as situações. Se você pode fazer algo como reparar um objeto ou marcá-lo como corrompido ou liberar algum recurso crítico, como um arquivo de bloqueio ou fechar um soquete de maneira limpa, esse é um bom uso de exceções. Se você realmente espera que o NULL seja exibido com frequência como um caso de borda válido, você deve lidar com isso no fluxo lógico normal com instruções if-the-else ou switch-case ou qualquer outra coisa, não com um tratamento de exceção. Por exemplo, um campo desabilitado em um formulário pode ser definido como NULL, que é diferente de uma sequência vazia que representa um campo deixado em branco. Esta é uma área em que você deve usar o bom senso e o bom senso. Eu nunca ouvi falar de boas e sólidas regras práticas sobre como lidar com esse problema em todas as situações.

O Java falha na implementação de manipulação de exceções com "exceções verificadas", onde todas as exceções possíveis que possam ser geradas por objetos usados ​​em um método devem ser capturadas ou declaradas na cláusula "throws" do método. O é o oposto de C ++ e Python, onde você pode optar por lidar com exceções como achar melhor. Em Java, se você modificar o corpo de um método para incluir uma chamada que possa gerar uma exceção, você terá a opção de incluir manipulação explícita para uma exceção que talvez você não se importe em adicionar ruído e complexidade ao código, ou você deve adicione essa exceção à cláusula "throws" do método que você está modificando, que não apenas adiciona ruído e confusão, mas altera a assinatura do seu método. Você deve, então, ir um código de modificação onde quer que seu método seja usado para lidar com a nova exceção que seu método agora pode gerar ou adicionar a exceção à cláusula "throws" desses métodos, provocando um efeito dominó das alterações de código. O "Requisito de captura ou especificação" deve ser um dos aspectos mais irritantes do Java. A exceção de exceção para RuntimeExceptions e a racionalização oficial do Java para esse erro de design é coxo.

Esse último parágrafo teve pouco a ver com responder à sua pergunta. Eu odeio Java.

- Noah


é difícil ler este post (parede de texto). Você se importaria de editá -lo em uma forma melhor?
Gnat 23/05
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.