Corretamente usando null
Existem diferentes maneiras de usar null
. A maneira mais comum e semanticamente correta é usá-lo quando você pode ou não ter um único valor. Nesse caso, um valor é igual null
ou é significativo como um registro do banco de dados ou algo assim.
Nessas situações, você geralmente o usa dessa maneira (em pseudo-código):
if (value is null) {
doSomethingAboutIt();
return;
}
doSomethingUseful(value);
Problema
E tem um problema muito grande. O problema é que, quando você invoca, doSomethingUseful
o valor pode não ter sido verificado null
! Caso contrário, o programa provavelmente falhará. E o usuário pode nem ver boas mensagens de erro, ficando com algo como "erro horrível: valor desejado, mas foi nulo!" (após a atualização: embora possa haver ainda menos erros informativos como Segmentation fault. Core dumped.
, ou pior ainda, nenhum erro e manipulação incorreta em null em alguns casos)
Esquecer de escrever verificações null
e lidar com null
situações é um bug extremamente comum . É por isso que Tony Hoare, que inventou, null
disse em uma conferência de software chamada QCon London em 2009 que ele cometeu o erro de um bilhão de dólares em 1965:
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Erro-Tony-Hoare
Evitando o problema
Algumas tecnologias e linguagens tornam null
impossível verificar o esquecimento de maneiras diferentes, reduzindo a quantidade de bugs.
Por exemplo, Haskell tem a Maybe
mônada em vez de nulos. Suponha que esse DatabaseRecord
seja um tipo definido pelo usuário. Em Haskell, um valor do tipo Maybe DatabaseRecord
pode ser igual Just <somevalue>
ou igual a Nothing
. Você pode usá-lo de diferentes maneiras, mas não importa como o use, não poderá aplicar alguma operação Nothing
sem conhecê-lo.
Por exemplo, esta função chamada zeroAsDefault
retorna x
para Just x
e 0
para Nothing
:
zeroAsDefault :: Maybe Int -> Int
zeroAsDefault mx = case mx of
Nothing -> 0
Just x -> x
Christian Hackl diz que C ++ 17 e Scala têm seus próprios caminhos. Portanto, você pode tentar descobrir se o seu idioma tem algo parecido e usá-lo.
Os nulos ainda são amplamente utilizados
Se você não tem nada melhor, o uso null
é bom. Apenas continue atento. Declarações de tipo em funções o ajudarão de alguma maneira.
Além disso, isso pode parecer pouco progressivo, mas você deve verificar se seus colegas querem usar null
ou algo mais. Eles podem ser conservadores e podem não querer usar novas estruturas de dados por alguns motivos. Por exemplo, suporte a versões mais antigas de um idioma. Tais coisas devem ser declaradas nos padrões de codificação do projeto e discutidas adequadamente com a equipe.
Na sua proposta
Você sugere o uso de um campo booleano separado. Mas você precisa verificá-lo de qualquer maneira e ainda pode esquecer de verificá-lo. Portanto, não há nada ganho aqui. Se você pode esquecer outra coisa, como atualizar os dois valores a cada vez, é ainda pior. Se o problema de esquecer de verificar null
não for resolvido, não faz sentido. Evitar null
é difícil e você não deve fazê-lo de uma maneira que a torne pior.
Como não usar null
Finalmente, existem maneiras comuns de usar null
incorretamente. Uma dessas maneiras é usá-lo no lugar de estruturas de dados vazias, como matrizes e seqüências de caracteres. Uma matriz vazia é uma matriz adequada como qualquer outra! É quase sempre importante e útil para estruturas de dados, que podem se ajustar a vários valores, para poder ficar vazio, ou seja, tem 0 comprimento.
Do ponto de vista da álgebra, uma string vazia para strings é semelhante a 0 para números, ou seja, identidade:
a+0=a
concat(str, '')=str
A string vazia permite que as strings em geral se tornem um monóide:
https://en.wikipedia.org/wiki/Monoid
Se você não conseguir, não é tão importante para você.
Agora vamos ver por que é importante programar com este exemplo:
for (element in array) {
doSomething(element);
}
Se passarmos uma matriz vazia aqui, o código funcionará bem. Isso não fará nada. No entanto, se passarmos null
aqui, provavelmente teremos um erro com um erro do tipo "não é possível fazer um loop nulo, desculpe". Poderíamos envolvê-lo, if
mas isso é menos limpo e, novamente, você pode esquecer de verificá-lo
Como lidar com null
O que doSomethingAboutIt()
deveria estar fazendo e, principalmente, se deveria lançar uma exceção é outra questão complicada. Em resumo, depende se null
foi um valor de entrada aceitável para uma determinada tarefa e o que é esperado em resposta. Exceções são para eventos que não eram esperados. Não irei mais além nesse tópico. Essa resposta já é longa.
std::optional
ouOption
. Em outros idiomas, você pode ter que criar um mecanismo apropriado, você mesmo pode recorrer anull
algo semelhante, porque é mais idiomático.