Alternativas para return null

29 respostas
C

Quando um método por exemplo buscaUsuarioPor(id) não encontra um usuario com aquele id o que deveriamos retornar?
No caso de coleções de objetos o Collections.emptyList() é sempre minha primeira opção, mas no caso de objetos unicos sempre fico na dúvida. Acho estranho retornar null, li alguns artigos onde falavam para retornar objetos vazios ou instancias que herdam de Usuario por exemplo onde verificariamos que aquela é uma instancia de um Usuario invalido… Mas nesse ultimo caso talvez seja apenas a troca de uma consulta == null por instanceOf…

Gostaria de conhecer a opnião de vocês nesse assunto, conhecer novas idéias para resolver esse problema, e encontrar uma forma mais elegante de tratar esses casos.

29 Respostas

A

Uma consulta que retornaria uma lista, retorna uma lista vazia. Uma consulta de resultado único, retorna null.

A

Talvez uma boa alternativa seja o Null Object pattern.

Tem um exemplo legal aqui

F

Um referencia apontando para null quer dizer que não existe objeto para ser manipulado. Ou seja, não tem nada haver com elegância ou não. É uma questão de semântica. Como vc acha q deveria se apontar um objeto que não existe? kkkk

  • Ou retorna nullo, em caso da consulta poder não encontrar o registro
  • ou lança uma exception de aplicação caso ser obrigatória a existência do objeto dentro do contexto da solução.
A

FernandoFranzini:
Um referencia apontando para null quer dizer que não existe objeto para ser manipulado. Ou seja, não tem nada haver com elegância ou não. É uma questão de semântica. Como vc acha q deveria se apontar um objeto que não existe? kkkk

  • Ou retorna nullo, em caso da consulta poder não encontrar o registro
  • ou lança uma exception de aplicação caso ser obrigatória a existência do objeto dentro do contexto da solução.

Talvez

  • Ou trazer um objeto “vazio”

Gostei da abordagem do Null Object pattern citado pelo allandequeiroz.
Acho que é uma boa alternativa. Como é uma boa prática retornar uma lista vazia ao invés de null em um método, tb acho válido retornar um usuário “vazio” ao invés de null.

J

FernandoFranzini:
Um referencia apontando para null quer dizer que não existe objeto para ser manipulado. Ou seja, não tem nada haver com elegância ou não. É uma questão de semântica. Como vc acha q deveria se apontar um objeto que não existe? kkkk

  • Ou retorna nullo, em caso da consulta poder não encontrar o registro
  • ou lança uma exception de aplicação caso ser obrigatória a existência do objeto dentro do contexto da solução.

Pelo contrário, o null é totalmente fora da semântica. Por exemplo, eu tenho um método que retorna o usuário autenticado ou ele traz o usuário correto ou um usuário vazio. Null é um conceito de programação, fora do domínio. Até porque null em java não é objeto…ele pode ser qualquer coisa. Ao contrário do NullUser que falaram que é um usuário vazio, é muito mais semântico e na hora de verificar é muito mais fácil ler e não dar NPE (NullPointerException)

Também acho que exceção é usado apenas para…exceções :smiley:

O usuário não conseguir se autenticar não é exceção é algo que pode ocorrer…usar exceção para fazer uma comunicação ao usuário não é o caminho eu acho…

F

é claro que não é mesmo…como seria um objeto nullo? kkkkkk
Nullo é ausência de objeto, que tb deve ser tratado corretamente dentro na logica do programa…como seria nunca ter um ponteiro nulo dentro do seu programa?
Eu discordo completamente…inclusive na minha prova de SCJD usei intensivamente essa abordagem e não perdi nota.
Eu acho que usar o patter null é legal sim, super elegante…mas tratar null da forma simples e básica que ele é tb é um boa pratica sim.
Fica a criterio de cada um usar o mais coerente para seu projeto…
T+

J

é claro que não é mesmo…como seria um objeto nullo? kkkkkk

Só um adendo, em Ruby nil (que é o null) é um objeto. (Sim, você pode fazer acessar métodos dele) :smiley:

E sim é possível minimizar bastante os null do seu projeto e fica bom viu? Você deveria tentar

As certificações nem sempre dizem o correto na hora de programar, até porque o Joshua Bloch no seu livro “Effective Java” fala sobre isso de evitar de usar null, principalmente em coleções.

Mas cada um, cada um né?

F

Não acho não…se fosse assim que valor teria não é mesmo? So para te esclarecer, na prova SCJD não existe correto ou errado…vc precisa defender sua decisão coerentemente, com justificativas arquiteturais.

Acredito que se a abordagem garantir nos caso a robustez ok. Não existe opção melhor…e sim trabalhar com elas, seus pros e contras.
A decisão fica aberta para cada um…

V

No livro Clean Code, Uncle Bob colocou dois tópicos para falar sobre isso: Don’t Return Null e Don’t Pass Null. Em resumo, se você usar null, muito provavelmente você precisa verificar se um objeto retornado é nulo, ou pegar uma possível NullPointerException (às vezes, tem de fazer os dois). É trabalho desnecessário e nada mais, não é uma boa prática.

Creio que uma prova disso são as formas propostas para evitar seu uso, como o NullObject já sugerido, ou o uso de ferramentas como Objects.firstNonNull do guava.

J

Não acho não…se fosse assim que valor teria não é mesmo? So para te esclarecer, na prova SCJD não existe correto ou errado…vc precisa defender sua decisão coerentemente, com justificativas arquiteturais.

É por isso que tem gente que defende que certificação não quer dizer muita coisa…mas vamos voltar ao tópico certo? O importante da discussão é ter os dois lados mostrando os seus argumentos.

Vamos esperar para ver quem mais se habilitar a discutir com a gente. :smiley:

Ah e só mais uma coisa, robustez é importante, mas um bom design de classes, código limpo e de fácil manutenção são tão importantes quanto. (Por isso que tento minimizar os nulls, eles poluem o código, deixa mais difícil de ler e obrigam a fazer mais ifs)

F

Agora sim temos uma boa justificativa arquitetural… parabéns!!! era isso que eu esperava de vc…Veja que eu não estava defendendo a certificação…na verdade a sua justificativa foi invalidar a credencialidade da certificação que no caso não tem nada relacionado com o assunto.
Vale lembrar que tem os contras tb…mesmo retornando o objetoNull polimórfico a aplicação ainda sim pode ter comportamentos não adequados, uma vez q esse objeto NULL não pode se comportar como tal durante a execução do programa.

J

Fernando, em momento nenhum quis descredenciar a certificação, só acho ruim o argumento de programo assim porque eu fiz na certificação e eles aceitaram.

Certificação prova muitas coisas, mas não se você sabe programar, e quando falei que tem pessoas (e empresas) que não ligam pra certificação é uma realidade de mercado, existem empresas que preferem ver o seu código do que monte de papel. Assim como existem empresas, que procuram profissionais certificados. Enfim, mas não é essa a questão do tópico.

Sei que a abordagem que comentei tem suas falhas, mas ainda acho melhor do que usar o puro e simplesmente null, porque acho que tem mais pontos contras do que positivos.

Por isso, estou acompanhando esse tópico…quero ver quais outras abordagens existem.

F

Então javablue a questão é que na SCJD é uma certificação diferenciada, uma vez que nela vc é obrigado a escrever um solução parcial e seu código (clareza, flexibilidade, OOP, bla, bla, bla etc) é avaliado sim. Procure mais informações (http://fernandofranzini.wordpress.com/2010/12/07/dicas-para-oracle-certified-master-java-developer/). Mas voltando ao assunto, existem 3 opções até aqui:

  1. Usar null
  2. Usar null patter
  3. Não usar null lançando exceptions.
    Alguem ve outras?
A

FernandoFranzini:
Mas voltando ao assunto, existem 3 opções até aqui:

  1. Usar null
  2. Usar null patter
  3. Não usar null lançando exceptions.
    Alguem ve outras?

Particulamente, gosto da idéia do NullPattern (principalmente em Java).

Mas você pode também retornar Talvez.

L

Discusões assim duram vidas inteiras…

MINHA OPINIÂO

se o retorno for uma lista creio q o ideial é retornar um List instanciado porem vazio (isEmpty() == true)
no caso de um unico registro creio que se deva retornar nulo.

Penso assim pelo fluxo simples da logica, busquei um registro, ele não existe então é nulo e não um registro q existe porem é vazio.

So para constar, o Hibernate possui dois metodos para carregar objetos pela pk. o metodo get (acessado pelo objeto da session) e o metodo load (tambem acessado pelo objeto session). O get qnd não encontra nenhum registro baseado na pk passada ele retorna nulo. Já o load lança uma Exceção.

F

Penso assim pelo fluxo simples da logica, busquei um registro, ele não existe então é nulo e não um registro q existe porem é vazio.
So para constar, o Hibernate possui dois metodos para carregar objetos pela pk. o metodo get (acessado pelo objeto da session) e o metodo load (tambem acessado pelo objeto session). O get qnd não encontra nenhum registro baseado na pk passada ele retorna nulo. Já o load lança uma Exceção.

Tb tenho a mesma opinião… :smiley:

P

Pedes para alguém ir buscar uma cerveja ao frigorífico.

Opções possíveis

  • Não trazer nenhuma cerveja porque não há.
  • Trazer uma garrafa vazia ou um desenho de uma garrafa ou qualquer coisa que seja só porque não há nenhuma cerveja.

Qual vos parece melhor e mais acertada?

G

Para coleções acho que já é consenso que seja retornada uma instância vazia.

Agora para objetos individuais, minha opinião é que depende do contexto.
Sempre que fizer sentido para o domínio a existência de um NullObject, utilize (exemplo do SecurityManager no livro do Joshua Block).
Se a inexistência do objeto constitui uma situação excepcional / erro de negócio, lance exceção (por exemplo, um método de login que retorna o usuário após validação dos dados).
Caso contrário, simplesmente é uma procura que não achou resultado, retorne null.

Ah… acrescentando: Se em muitos momentos diferentes tiver que ser feito o famoso if obj != null , é mais um motivo para favorecer o uso de NullObject.

A

pmlm:
Pedes para alguém ir buscar uma cerveja ao frigorífico.

Opções possíveis

  • Não trazer nenhuma cerveja porque não há.
  • Trazer uma garrafa vazia ou um desenho de uma garrafa ou qualquer coisa que seja só porque não há nenhuma cerveja.

Qual vos parece melhor e mais acertada?

Não entendi sua analogia. Você está defendendo ou criticando o uso do null?

Traduzindo para o java, retornar null é diferente de avisar que não tem cerveja.
Quando seu método retorna null, não há indicação que não tinha cerveja, ele executou normalmente.

Você só descobriria que não é realmente cerveja ao tentar usar e algo de excepcional ocorresse (as NPE do java).

Da mesma forma, quando você pede uma cerveja ao garçom e ele te traz uma garrafa, não é necessário perguntar:

  • Isso aqui é uma cerveja?

Se ele te trouxe silenciosamente a garrafa, espera-se que seja cerveja.

Pela sua analogia, o mais correto seria lançar uma exceção Não Tem Mais Cerveja, que seria o garçom avisando que acabou.

P

Exacto. Da mesma forma que se um método me devolve um objecto eu espero que seja o que eu pedi.
Se não existe o que pedi, devolve null ou, se não fizer sentido não exisitir, devolve uma excepção.

A

pmlm:

Exacto. Da mesma forma que se um método me devolve um objecto eu espero que seja o que eu pedi.
Se não existe o que pedi, devolve null ou, se não fizer sentido não exisitir, devolve uma excepção.

Então, mas quando ele te devolve null, não está devolvendo o que você pediu.
Estou esperando uma cerveja e vem algo diferente que não posso utilizar como cerveja.

Seria o mesmo de conferir com o garçom se o que veio é realmente cerveja.

Nesse contexto jogar a exceção que não tem mais cerveja seria mais coerente, na minha opinião.

G

Isso depende do contrato. Se colocarmos em um cartaz-javadoc no meio do salão: “O garçom retornará de mãos vazias quando a cerveja da marca solicitada não estiver disponível no estoque”, então o null passa a ter este significado.
Também poderia lançar uma BeerNotFoundException, como vc sugeriu.
Ou então trazer uma NullBeer que é a garrafa vazia…

A escolha da melhor maneira depende do contexto. Claro que na analogia da cerveja eu preferiria que ele retornasse uma mensagem, qualquer uma das outras opções seria um pouco ridícula rsrs.

Mas enfim…
O importante é não beber a noite inteira e na hora da conta tentar lançar uma MoneyNotFoundException, aí o bicho pega pro seu lado :smiley:

P

Voltando ao tema,
se um método pode retornar null, então antes de utilizar o seu resultado eu tenho de verificar se ele é null.
Porque hei-de implementar uma classe para resolver um problema que eu já resolvo com um if?

C

Pensando dessa forma, porque hei de utilizar polimorfismo se resolvo isso com um if?

Se trabalhamos com objetos, porque de repente parar de utiliza-los?

P

Não paramos de utiliza-los.
Se um objecto não existe, para mim, não faz sentido devolver um outro objecto. Mas isso sou eu… cada um é livre de pensar e fazer como acha melhor.

G

pmlm:
Voltando ao tema,
se um método pode retornar null, então antes de utilizar o seu resultado eu tenho de verificar se ele é null.
Porque hei-de implementar uma classe para resolver um problema que eu já resolvo com um if?

Esses IFs podem ser evitados desde que faça sentido para o negócio que está sendo modelado. Vou mostrar um exemplo:

Você tem um site onde os usuários podem entrar como Visitante ou com um login de usuário registrado.
O usuário registrado tem acesso a diversas funcionalidades dependendo de seu perfil, e o usuário visitante não tem acesso a nenhuma das funcionalidades restritas.
Quando o usuário se loga, um objeto do tipo User é armazenado na sessão, e contém as permissões deste usuário que podem ser verificadas através da chamada de um método.

User user = session.getAttribute("user");
if (user.permiteTransacaoXXX()) {
// .... Faz operação restrita
}

Uma primeira maneira de tratar os usuários visitantes seria colocar um null na sessão toda vez que alguém clica em “Entrar como visitante”. Aí para evitar o nullpointerexception, o trecho acima ficaria assim:

User user = session.getAttribute("user");
if (user != null && user.permiteTransacaoXXX()) {
// .... Faz operação restrita
}

Esse teste adicional se espalharia por todo o código… porém há um jeito mais elegante. Quando um usuário clica em “Entrar como visitante”, armazenamos na Sessão um usuário com todas as transações desabilitadas, de modo que as chamadas aos métodos permiteTransacaoXXX() sempre retornem false.
Assim fica garantido que sempre há uma instância de User na sessão, e podemos voltar ao código mais simples.

User user = session.getAttribute("user");
if (user.permiteTransacaoXXX()) {
// .... Faz operação restrita
}

E pronto! Esse usuário visitante é um NullObject. É uma solução que surge naturalmente, e que faz todo o sentido dentro do negócio.

P

Neste caso não tinhamos o caso de um utilizador não existente mas sim utilizador visitante. São coisas diferentes.

G

Você se refere a “Utilizador não existente” no sentido de um usuário que não existe no banco de dados? Se sim, esse é um outro caso, um erro no processo de login.

No meu exemplo, não estou falando de um “usuário não existente” porque o login é inválido, mas sim de um caso de usuário inexistente na sessão de utilização atual, ou em outras palavras, estou navegando sem um usuário.

Na situação a que você se refere, creio que uma Exception seria o melhor tratamento.
Em meu exemplo, temos um caso que seria resolvido com null ou um NullObject.

Pois é justamente isso, o UsuarioVisitante é o NullObject de Usuario! Trata-se de uma instância do objeto com comportamento “neutro” ou “nulo”. (Repare que está de acordo com a definição do pattern NullObject).

É um exemplo bem semelhante ao que está no livro do Joshua Block, em que ele sugere que a VM deveria fornecer um LaxSecurityManager quando a aplicação estiver sendo executada sem nenhum SecurityManager.


Ah, só reiterando uma coisa aqui: estou apenas dando um exemplo válido de utilização do NullObject, sei que é uma situação diferente da descrita pelo autor do tópico, no caso uma pesquisa que não retorna o registro esperado.

G

Essa discussão é interessante para aqueles que gostam de escovar bytes. Um colega aqui citou o tal do “NIL” que siginifica “NULL” em Ruby, e esse cara realmente é um objeto e não um ponteiro sem instância.

Em Python, todas as classes estendes PyObject, assim como em Java todas as classes estendem Object. Então eu posso ao invés de retornar nulo, retornar um objeto do tipo PyObject ou Object. Assim evitaríamos o nulo. Porém em Java isso não é possível, por que a linguagem é do tipo Compilada enquanto que Ruby e Phyton são linguagens Interpretadas. Você até pode retornar um “new Object()” em caso de nulo, mas aí seria preciso fazer um “cast” e antes do cast fazer um IF. Ou seja, voltaria a estaca zero.

Retornar uma instância do mesmo objeto, vazio, seria uma “boa” prática, mas nem todo objeto permite uma sua criação sem “estado e métodos”. O que temos no final é uma restrição da linguagem Java, que por hora nos força a encher o código com “xxx != null”.

Criado 17 de novembro de 2011
Ultima resposta 21 de nov. de 2011
Respostas 29
Participantes 12