Evitando! = Instruções nulas

4015

Eu uso object != nullmuito para evitar NullPointerException.

Existe uma boa alternativa para isso?

Por exemplo, eu costumo usar:

if (someobject != null) {
    someobject.doCalc();
}

Isto verifica para um NullPointerExceptionpara o someobjectobjecto no fragmento acima.

Observe que a resposta aceita pode estar desatualizada, consulte https://stackoverflow.com/a/2386013/12943 para obter uma abordagem mais recente.

Goran Martinic
fonte
120
@ Shervin Encorajar nulos torna o código menos compreensível e menos confiável.
Tom Hawtin - tackline
44
Os operadores Elvis foram propostos, mas parece que não será no Java 7 . Que pena, ?. ?: e? [] economizam tempo incrível.
24511 Scott
72
Não usar null é superior à maioria das outras sugestões aqui. Lance exceções, não retorne nem permita valores nulos. BTW - a palavra-chave 'assert' é inútil, porque está desativada por padrão. Use um mecanismo de falha sempre ativado
ianpojman
13
Essa é uma das razões pelas quais agora estou usando o Scala. Em Scala, tudo não é anulável. Se você deseja permitir passar ou retornar "nada", é necessário usar a opção [T] em vez de apenas o argumento ou o tipo de retorno.
Thekwasti 02/02
11
@thSoft De fato, o incrível Optiontipo de Scala é marcado pela falta de vontade da língua em controlar ou proibir null. Eu mencionei isso no hackernews e recebi votos negativos e disse que "ninguém usa null no Scala de qualquer maneira". Adivinha, já estou encontrando nulos no Scala escrito por um colega de trabalho. Sim, eles estão "fazendo errado" e deve ser educado sobre isso, mas a verdade é sistema do tipo da linguagem deve proteger-me com isso e isso não acontece :(
Andres F.

Respostas:

2644

Isso para mim parece um problema razoavelmente comum que os desenvolvedores juniores a intermediários tendem a enfrentar em algum momento: eles não sabem ou não confiam nos contratos dos quais estão participando e buscam na defensiva por nulos. Além disso, ao escrever seu próprio código, eles tendem a confiar no retorno de nulos para indicar algo, exigindo que o chamador verifique nulos.

Em outras palavras, há duas instâncias em que a verificação nula é exibida:

  1. Onde nulo é uma resposta válida em termos do contrato; e

  2. Onde não é uma resposta válida.

(2) é fácil. Use assertinstruções (asserções) ou permita falha (por exemplo, NullPointerException ). As asserções são um recurso Java altamente subutilizado que foi adicionado na 1.4. A sintaxe é:

assert <condition>

ou

assert <condition> : <object>

onde <condition>é uma expressão booleana e <object>é um objeto cuja toString()saída do método será incluída no erro.

Uma assertinstrução lança um Error( AssertionError) se a condição não for verdadeira. Por padrão, Java ignora asserções. Você pode ativar asserções passando a opção -eapara a JVM. Você pode ativar e desativar asserções para classes e pacotes individuais. Isso significa que você pode validar o código com as asserções durante o desenvolvimento e o teste e desabilitá-las em um ambiente de produção, embora meus testes tenham demonstrado quase nenhum impacto no desempenho das asserções.

Não usar afirmações nesse caso é bom porque o código falhará, o que acontecerá se você usar afirmações. A única diferença é que, com afirmações, isso pode acontecer mais cedo, de maneira mais significativa e possivelmente com informações extras, o que pode ajudá-lo a descobrir por que isso aconteceu se você não estava esperando.

(1) é um pouco mais difícil. Se você não tem controle sobre o código para o qual está ligando, está preso. Se nulo é uma resposta válida, você deve procurá-la.

No entanto, se é o código que você controla (e esse geralmente é o caso), é uma história diferente. Evite usar nulos como resposta. Com métodos que retornam coleções, é fácil: retornar coleções vazias (ou matrizes) em vez de nulos praticamente o tempo todo.

Com as não coleções, pode ser mais difícil. Considere isso como um exemplo: se você possui estas interfaces:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

onde o Analisador pega a entrada bruta do usuário e encontra algo para fazer, talvez se você estiver implementando uma interface de linha de comando para algo. Agora você pode fazer com que o contrato retorne nulo se não houver uma ação apropriada. Isso leva à verificação nula da qual você está falando.

Uma solução alternativa é nunca retornar nulo e, em vez disso, usar o padrão Null Object :

public class MyParser implements Parser {
  private static Action DO_NOTHING = new Action() {
    public void doSomething() { /* do nothing */ }
  };

  public Action findAction(String userInput) {
    // ...
    if ( /* we can't find any actions */ ) {
      return DO_NOTHING;
    }
  }
}

Comparar:

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
  // do nothing
} else {
  action.doSomething();
}

para

ParserFactory.getParser().findAction(someInput).doSomething();

que é um design muito melhor porque leva a um código mais conciso.

Dito isso, talvez seja inteiramente apropriado que o método findAction () lance uma exceção com uma mensagem de erro significativa - especialmente nesse caso em que você depende da entrada do usuário. Seria muito melhor para o método findAction lançar uma exceção do que para o método de chamada explodir com uma simples NullPointerException sem explicação.

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

Ou, se você acha que o mecanismo de tentativa / captura é muito feio, em vez de Não fazer nada, sua ação padrão deve fornecer feedback ao usuário.

public Action findAction(final String userInput) {
    /* Code to return requested Action if found */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action not found: " + userInput);
        }
    }
}
cletus
fonte
631
Não concordo com suas declarações para a ação DO_NOTHING. Se o método de ação de localização não puder encontrar uma ação, retornar nulo é a coisa certa a fazer. Você "encontrou" uma ação no seu código que não foi realmente encontrada, o que viola o princípio do método, para encontrar uma ação utilizável.
MetroidFan2002 /
266
Concordo que nulo é usado em excesso em Java, especialmente com listas. Muitas APIs seriam melhores se elas retornassem uma lista / matriz / coleção vazia em vez de nula. Muitas vezes, nulo é usado onde uma exceção deve ser lançada. Uma exceção deve ser lançada se o analisador não puder analisar.
Laplie Anderson 14/11/08
92
O último exemplo aqui é, IIRC, o padrão de design de Objeto Nulo.
Steven Evers
62
@Cshah (e MetroidFan2002). Simples, coloque isso no contrato e fica claro que uma ação retornada não encontrada não fará nada. Se essas informações forem importantes para o chamador, forneça uma maneira de descobrir que foi uma ação não encontrada (por exemplo, forneça um método para verificar se o resultado foi o objeto DO_NOTHING). Como alternativa, se a ação normalmente for encontrada, você ainda não deve retornar nulo, mas sim lançar uma exceção indicando especificamente essa condição - isso ainda resulta em um código melhor. Se desejar, forneça um método separado retornando booleano para verificar se existe alguma ação.
Kevin Brock
35
Conciso não é igual ao código de qualidade. Sinto muito que você pense assim. Seu código oculta situações em que um erro seria vantajoso.
Gshauger
635

Se você usar (ou planejar usar) um Java IDE como JetBrains IntelliJ IDEA , Eclipse ou Netbeans ou uma ferramenta como findbugs, poderá usar anotações para resolver esse problema.

Basicamente, você tem @Nullablee @NotNull.

Você pode usar métodos e parâmetros, como este:

@NotNull public static String helloWorld() {
    return "Hello World";
}

ou

@Nullable public static String helloWorld() {
    return "Hello World";
}

O segundo exemplo não será compilado (no IntelliJ IDEA).

Quando você usa a primeira helloWorld()função em outro trecho de código:

public static void main(String[] args)
{
    String result = helloWorld();
    if(result != null) {
        System.out.println(result);
    }
}

Agora, o compilador IntelliJ IDEA informará que a verificação é inútil, pois a helloWorld()função nunca retornará null.

Usando parâmetro

void someMethod(@NotNull someParameter) { }

se você escrever algo como:

someMethod(null);

Isso não será compilado.

Último exemplo usando @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Fazendo isso

iWantToDestroyEverything().something();

E você pode ter certeza de que isso não acontecerá. :)

É uma boa maneira de permitir que o compilador verifique algo mais do que normalmente faz e impor seus contratos para serem mais fortes. Infelizmente, não é suportado por todos os compiladores.

No IntelliJ IDEA 10.5 e posteriores, eles adicionaram suporte para qualquer outro @Nullable @NotNull implementações.

Ver postagem no blog Anotações @ flexíveis e configuráveis ​​@ Nullable / @ NotNull .

Luca Molteni
fonte
120
@NotNull, @NullableE outras anotações nullness fazem parte do JSR 305 . Você também pode usá-los para detectar possíveis problemas com ferramentas como o FindBugs .
Jacek S
32
Acho estranhamente irritante que as interfaces @NotNull& @Nullableestejam no pacote com.sun.istack.internal. (Eu acho que eu com.sun associado com avisos sobre usando uma API proprietária.)
Jonik
20
A portabilidade do código fica nula com os jetbrains. Eu pensaria duas vezes (quadrado) antes de amarrar no nível do ide.
Java Ka Baby
10
Realmente não acho que o uso de um compilador personalizado seja uma solução viável para esse problema.
Shivan Dragon
64
O lado bom das anotações, que são @NotNulle @Nullablesão, é que elas se degradam muito bem quando o código-fonte é criado por um sistema que não as entende. Portanto, com efeito, o argumento de que o código não é portátil pode ser inválido - se você usar um sistema que suporte e compreenda essas anotações, obterá o benefício adicional de uma verificação mais rigorosa dos erros, caso contrário, você obtém menos, mas seu código ainda deve compilar bem, e a qualidade do seu programa em execução é A MESMA, porque essas anotações não foram aplicadas em tempo de execução de qualquer maneira. Além disso, todos os compiladores são personalizados ;-) #
1120
321

Se valores nulos não forem permitidos

Se seu método for chamado externamente, comece com algo como isto:

public void method(Object object) {
  if (object == null) {
    throw new IllegalArgumentException("...");
  }

Então, no restante desse método, você saberá que object não é nulo.

Se for um método interno (não parte de uma API), apenas documente que não pode ser nulo, e é isso.

Exemplo:

public String getFirst3Chars(String text) {
  return text.subString(0, 3);
}

No entanto, se o seu método apenas passar o valor, e o próximo método o transmitir, etc., isso pode se tornar problemático. Nesse caso, você pode querer verificar o argumento como acima.

Se nulo for permitido

Isso realmente depende. Se achar que muitas vezes faço algo assim:

if (object == null) {
  // something
} else {
  // something else
}

Então eu ramifico e faço duas coisas completamente diferentes. Não há um trecho de código feio, porque eu realmente preciso fazer duas coisas diferentes, dependendo dos dados. Por exemplo, devo trabalhar na entrada ou devo calcular um bom valor padrão?


Na verdade, é raro eu usar o idioma "if (object != null && ... ".

Pode ser mais fácil dar exemplos, se você mostrar exemplos de onde normalmente usa o idioma.

myplacedk
fonte
94
Qual é o sentido de lançar IllegalArgumentException? Eu acho que NullPointerException seria mais claro, e isso também seria lançado se você não fizer a verificação nula. Eu usaria afirmação ou nada.
Axel
23
É improvável que todos os outros valores que nulos sejam aceitáveis. Você pode ter IllegalArgumentException, OutOfRageException etc etc. Às vezes isso faz sentido. Outras vezes, você acaba criando muitas classes de exceção que não agregam valor, e então usa IllegalArgumentException. Não faz sentido ter uma exceção para entrada nula e outra para todo o resto.
Myplacedk
7
Sim, concordo com o princípio de falha rápida, mas no exemplo acima, o valor não é passado adiante, mas é o objeto no qual um método deve ser chamado. Portanto, ele falha igualmente rápido e a adição de uma verificação nula apenas para gerar uma exceção que seria lançada de qualquer maneira ao mesmo tempo e local não parece facilitar a depuração.
Axel
8
Uma brecha na segurança? O JDK está cheio de código como este. Se você não deseja que o usuário veja os rastreamentos de pilha, desative-os. Ninguém sugeriu que o comportamento não está documentado. O MySQL é escrito em C, onde a exclusão de ponteiros nulos é um comportamento indefinido, nada como lançar uma exceção.
fgb
8
throw new IllegalArgumentException("object==null")
Thorbjørn Ravn Andersen
238

Uau, eu quase odeio adicionar outra resposta quando temos 57 maneiras diferentes de recomendar o NullObject pattern, mas acho que algumas pessoas interessadas nesta pergunta talvez gostem de saber que existe uma proposta na tabela para o Java 7 adicionar "segurança nula" tratamento" - uma sintaxe simplificada para lógica if-not-equal-null.

O exemplo dado por Alex Miller é assim:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

Os ?.meios apenas fazem a referência do identificador esquerdo se não for nulo; caso contrário, avalie o restante da expressão como null. Algumas pessoas, como Dick Wall, membro do Java Posse e os eleitores da Devoxx, realmente amam essa proposta, mas também há oposição, alegando que ela realmente incentivará um maior uso nullcomo valor sentinela.


Atualização: Uma proposta oficial para um operador com segurança nula no Java 7 foi enviada no Project Coin. A sintaxe é um pouco diferente do exemplo acima, mas é a mesma noção.


Atualização: a proposta do operador com segurança nula não foi incluída no Project Coin. Portanto, você não verá esta sintaxe no Java 7.

erickson
fonte
20
Acho que isso está errado. Deve haver uma maneira de especificar que uma determinada variável SEMPRE não seja nula.
Thorbjørn Ravn Andersen
5
Atualização: a proposta não fará o Java7. Consulte blogs.sun.com/darcy/entry/project_coin_final_five .
Boris Terzic 29/08/2009
9
Idéia interessante, mas a escolha da sintaxe é absurda; Não quero uma base de código cheia de pontos de interrogação afixados em todas as articulações.
1026 Rob
7
Esse operador existe no Groovy , portanto, quem deseja usá-lo ainda o tem como opção.
22413 Muhd
8
Esta é a ideia mais engenhosa que já vi. Ele deve ser adicionado a todos os idiomas sensíveis da sintaxe C. Prefiro "fixar pontos de interrogação" em todos os lugares do que percorrer a tela cheia de linhas ou evitar "cláusulas de guarda" o dia todo.
Victor
196

Se valores indefinidos não forem permitidos:

Você pode configurar seu IDE para avisá-lo sobre a possibilidade de cancelamento de referência nula. Por exemplo, no Eclipse, consulte Preferências> Java> Compilador> Erros / avisos / análise nula .

Se valores indefinidos forem permitidos:

Se você deseja definir uma nova API na qual valores indefinidos fazem sentido , use o Padrão de Opção (pode ser familiar nas linguagens funcionais). Tem as seguintes vantagens:

  • É declarado explicitamente na API se uma entrada ou saída existe ou não.
  • O compilador força você a lidar com o caso "indefinido".
  • Option é uma mônada , portanto não há necessidade de verificação nula detalhada, basta usar map / foreach / getOrElse ou um combinador similar para usar o valor com segurança (exemplo) .

Java 8 tem uma Optionalclasse interna (recomendado); para versões anteriores, existem alternativas de biblioteca, por exemplo, Guava ' Optionalou FunctionalJava 'Option . Porém, como muitos padrões de estilo funcional, o uso de Option em Java (até 8) resulta em bastante clichê, que você pode reduzir usando uma linguagem JVM menos detalhada, como Scala ou Xtend.

Se você precisar lidar com uma API que possa retornar nulos , não poderá fazer muito em Java. Xtend e Groovy têm o operador Elvis ?: e o operador de desreferência com segurança nula ?. , mas observe que isso retorna nulo no caso de uma referência nula; portanto, apenas "adia" a manipulação adequada de nulo.

thSoft
fonte
22
De fato, o padrão Opção é incrível. Existem alguns equivalentes Java. Goiaba contém uma versão limitada deste chamado opcional, que deixa de fora a maioria das coisas funcionais. Em Haskell, esse padrão é chamado de Talvez.
Ben Hardy
9
Uma classe opcional estará disponível em Java 8
Pierre Henry
1
... e ainda não possui mapa nem flatMap: download.java.net/jdk8/docs/api/java/util/Optional.html
thSoft
3
O padrão opcional não resolve nada; em vez de um objeto potencialmente nulo, agora você tem dois.
Boann
1
@Boann, se usado com cuidado, você resolve todos os seus problemas de NPEs. Caso contrário, acho que há um problema de "uso".
31816 Louis
189

Somente para esta situação -

Não verificando se uma variável é nula antes de chamar um método equals (um exemplo de comparação de string abaixo):

if ( foo.equals("bar") ) {
 // ...
}

resultará em um NullPointerExceptionse foonão existir.

Você pode evitar isso se comparar seus Strings assim:

if ( "bar".equals(foo) ) {
 // ...
}
echox
fonte
42
Eu concordo - apenas nessa situação. Eu não suporto programadores que levaram isso para o próximo nível desnecessário e escrevem if (null! = MyVar) ... apenas me parece feio e não serve para nada!
Alex Worden 23/05
20
Este é um exemplo particular, provavelmente o mais utilizado, de uma boa prática geral: se você souber, sempre faça <object that you know that is not null>.equals(<object that might be null>);. Funciona para outros métodos, exceto equalsse você conhece o contrato e esses métodos podem manipular nullparâmetros.
Stef
9
Este é o primeiro exemplo que tenho visto de Condições Yoda que realmente faz sentido
Erin Drummond
6
NullPointerExceptions são lançadas por um motivo. Eles são lançados porque um objeto é nulo onde não deveria estar. É o trabalho dos programadores corrigir isso, não ocultar o problema.
Oliver Watkins
1
Try-Catch-DoNothing esconde o problema, é uma prática válida para solucionar a falta de açúcar no idioma.
Echox 12/05
167

Com o Java 8, vem a nova java.util.Optionalclasse que, sem dúvida, resolve alguns dos problemas. Pode-se dizer pelo menos que isso melhora a legibilidade do código e, no caso de APIs públicas, torna o contrato da API mais claro para o desenvolvedor do cliente.

Eles funcionam assim:

Um objeto opcional para um determinado tipo ( Fruit) é criado como o tipo de retorno de um método. Pode estar vazio ou conter um Fruitobjeto:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Agora observe este código em que pesquisamos uma lista de Fruit( fruits) para uma determinada instância Fruit:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Você pode usar o map()operador para executar uma computação - ou extrair um valor de - um objeto opcional. orElse()permite fornecer um fallback para valores ausentes.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

Obviamente, a verificação do valor nulo / vazio ainda é necessária, mas pelo menos o desenvolvedor está consciente de que o valor pode estar vazio e o risco de esquecer de verificar é limitado.

Em uma API criada do zero, usando Optionalsempre que um valor de retorno estiver vazio e retornando um objeto simples somente quando não puder sernull (convenção), o código do cliente pode abandonar as verificações nulas nos valores de retorno de objetos simples ...

É claro Optionalque também poderia ser usado como argumento de método, talvez uma maneira melhor de indicar argumentos opcionais do que 5 ou 10 métodos de sobrecarga em alguns casos.

Optionaloferece outros métodos convenientes, como o orElseque permite o uso de um valor padrão e ifPresentque funciona com expressões lambda .

Convido você a ler este artigo (minha principal fonte para escrever esta resposta), na qual são explicadas tanto a NullPointerException(e geralmente o ponteiro nulo) problemática quanto a solução (parcial) apresentada por Optional: Objetos Opcionais Java .

Pierre Henry
fonte
11
A goiaba do Google tem uma implementação opcional para Java 6+.
Bradley Gottfried
13
É muito importante enfatizar que usar Opcional apenas com ifPresent () não adiciona muito valor acima da verificação nula normal. Seu principal valor é que é uma mônada que pode ser usada nas cadeias de funções do map / flapMap, que obtém resultados semelhantes ao operador Elvis no Groovy mencionado em outro lugar. Mesmo sem esse uso, acho a sintaxe orElse / orElseThrow também muito útil.
Cornel Masson
Este blog tem uma boa entrada em Opcional winterbe.com/posts/2015/03/15/avoid-null-checks-in-java
JohnC
4
Por que as pessoas têm tendência a fazer isso if(optional.isPresent()){ optional.get(); }em vez deoptional.ifPresent(o -> { ...})
Satyendra Kumar
1
Portanto, além das dicas contratuais da API, trata-se realmente de atender a programadores funcionais que gostam de encadear métodos sem parar.
esmagar
125

Dependendo do tipo de objeto que você está verificando, você pode usar algumas das classes nos apache commons, como: apache commons lang e apache commons collections

Exemplo:

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

ou (dependendo do que você precisa verificar):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

A classe StringUtils é apenas uma dentre muitas; existem algumas boas classes nos bens comuns que fazem manipulação segura nula.

Segue um exemplo de como você pode usar validação nula em JAVA ao incluir a biblioteca apache (commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

E se você estiver usando o Spring, o Spring também possui a mesma funcionalidade em seu pacote, consulte a biblioteca (spring-2.4.6.jar)

Exemplo de como usar este classf estático da spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
javamonkey79
fonte
5
Além disso, você pode usar a versão mais genérica do Apache Commons, bastante útil no início dos métodos para verificar os parâmetros que encontro. Validate.notNull (objeto, "o objeto não deve ser nulo"); commons.apache.org/lang/apidocs/org/apache/commons/lang/…
monojohnny
@monojohnny o Validate usa declarações Assert em ?. Eu pergunto isso porque o Assert pode ser ativado / desativado na JVM e é recomendável não usar na produção.
precisa
Não penso assim - Eu acredito que apenas lança uma RuntimeException se a validação falhar
monojohnny
96
  • Se você considerar que um objeto não deve ser nulo (ou é um bug), use uma declaração.
  • Se o seu método não aceitar parâmetros nulos, diga-o no javadoc e use uma declaração.

Você precisa verificar o objeto! = Null apenas se desejar lidar com o caso em que o objeto pode ser nulo ...

Há uma proposta para adicionar novas anotações no Java7 para ajudar com parâmetros nulos / não nulos: http://tech.puredanger.com/java7/#jsr308

pgras
fonte
91

Sou fã do código "falhar rápido". Pergunte a si mesmo - você está fazendo algo útil no caso em que o parâmetro é nulo? Se você não tiver uma resposta clara para o que seu código deve fazer nesse caso ... Ou seja, nunca deve ser nulo em primeiro lugar, ignore-o e permita que uma NullPointerException seja lançada. O código de chamada fará tanto sentido para um NPE quanto para um IllegalArgumentException, mas será mais fácil para o desenvolvedor depurar e entender o que deu errado se um NPE for lançado, em vez de seu código tentar executar alguma outra contingência inesperada lógica - que acaba resultando na falha da aplicação.

Alex Worden
fonte
2
melhor usar asserções, ou seja, Contract.notNull (abc, "abc deve ser não nulo, ele falhou ao carregar durante xyz?"); - esta é uma forma mais compacta do que fazer um if {throw new RuntimeException ...} (abc! = Null)
ianpojman
76

A estrutura de coleções do Google oferece uma maneira boa e elegante de realizar a verificação nula.

Existe um método em uma classe de biblioteca como esta:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

E o uso é (com import static):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

Ou no seu exemplo:

checkNotNull(someobject).doCalc();
user2427
fonte
71
mmm, qual é a diferença? p.getAge () lançaria o mesmo NPE com menos sobrecarga e um rastreamento de pilha mais claro. o que estou perdendo?
285 mysomic
15
É melhor lançar uma IllegalArgumentException ("e == null") no seu exemplo, pois indica claramente que é uma exceção pretendida pelo programador (junto com informações suficientes para permitir ao mantenedor identificar o problema). NullPointerExceptions deve ser reservado para a JVM, uma vez que, em seguida, indica claramente que este não foi intencional (e geralmente acontece em algum lugar difícil identificar)
Thorbjørn Ravn Andersen
6
Agora isso faz parte do Google Guava.
Steven Benitez
32
Cheira a excesso de engenharia para mim. Apenas deixe a JVM lançar um NPE e não bagunce seu código com esse lixo.
precisa
5
Eu gosto e abro a maioria dos métodos e construtores com verificações explícitas nos argumentos; se houver um erro, os métodos sempre falham nas primeiras linhas e eu conheço a referência ofensiva sem encontrar algo comogetThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");
Cory Kendall
75

Às vezes, você tem métodos que operam em seus parâmetros que definem uma operação simétrica:

a.f(b); <-> b.f(a);

Se você sabe que b nunca pode ser nulo, basta trocá-lo. É mais útil para iguais: em vez de foo.equals("bar");fazer melhor "bar".equals(foo);.

Johannes Schaub - litb
fonte
2
Mas então você deve assumir equals(pode ser qualquer método) manipulará o nulo corretamente. Realmente, tudo o que isso está fazendo é passar a responsabilidade para outra pessoa (ou outro método).
Supericy
4
@ Supericy Basicamente sim, mas equals(ou qualquer outro método) precisa verificar de nullqualquer maneira. Ou declare explicitamente que não.
Angelo Fuchs
74

Em vez de Padrão de Objeto Nulo - que tem seus usos - você pode considerar situações em que o objeto nulo é um erro.

Quando a exceção é lançada, examine o rastreamento da pilha e trabalhe com o bug.

Jim Nelson
fonte
17
O problema é que geralmente você perde o contexto, pois a NullPointerException não indica que a variável WHICH era nula e você pode ter várias operações "." Na linha. O uso de "if (foo == null) throw new RuntimeException (" foo == null ")" permite que você indique explicitamente o QUE estava errado, dando ao seu rastreamento de pilha muito mais valor para aqueles que precisam corrigi-lo.
Thorbjørn Ravn Andersen
1
Com Andersen - eu adoraria que o sistema de exceção do Java pudesse incluir o nome de uma variável que está sendo trabalhada, para que NullPointerExceptions indicasse não apenas a linha em que a exceção ocorreu, mas também o nome da variável. Isso deve funcionar bem em software não ofuscado.
Fwielstra
Ainda não consegui fazê-lo funcionar, mas isso visa solucionar exatamente esse problema.
MatrixFrog
Qual é o problema? Basta pegar o NPE no nível apropriado, onde há contexto suficiente disponível, despejar as informações de contexto e repetir a exceção ... é tão fácil com Java.
precisa saber é o seguinte
3
Eu tive um professor que pregou contra o encadeamento de chamadas de método. A teoria dele era que você deveria desconfiar de cadeias de chamadas com mais de 2 métodos. Não sei se essa é uma regra difícil, mas definitivamente remove a maioria dos problemas com os rastreamentos de pilha do NPE.
RustyTheBoyRobot 16/04
74

Nulo não é um 'problema'. É parte integrante de um conjunto completo de ferramentas de modelagem. O software tem como objetivo modelar a complexidade do mundo e nulo carrega seu fardo. Nulo indica 'Sem dados' ou 'Desconhecido' em Java e similares. Portanto, é apropriado usar nulos para esses fins. Não prefiro o padrão 'Objeto nulo'; Eu acho que surge o problema de ' quem guardará os guardiões '.
Se você me perguntar qual é o nome da minha namorada, eu direi que não tenho namorada. Na linguagem Java retornarei nulo. Uma alternativa seria lançar uma exceção significativa para indicar algum problema que não pode ser (ou não

  1. Para uma "pergunta desconhecida", dê "resposta desconhecida". (Seja nulo com segurança quando isso estiver correto do ponto de vista comercial) A verificação de argumentos como nulo uma vez dentro de um método antes do uso evita que vários chamadores os verifiquem antes de uma chamada.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }

    O anterior leva ao fluxo lógico normal para não obter uma foto de uma namorada inexistente da minha biblioteca de fotos.

    getPhotoOfThePerson(me.getGirlfriend())

    E se encaixa com a nova API Java que está chegando (ansiosa)

    getPhotoByName(me.getGirlfriend()?.getName())

    Embora seja bastante "fluxo comercial normal" não encontrar fotos armazenadas no banco de dados para alguma pessoa, eu costumava usar pares como abaixo em outros casos

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);

    E não deteste digitar <alt> + <shift> + <j>(gere javadoc no Eclipse) e escreva três palavras adicionais para sua API pública. Isso será mais do que suficiente para todos, exceto aqueles que não leem a documentação.

    /**
     * @return photo or null
     */

    ou

    /**
     * @return photo, never null
     */
  2. Esse é um caso bastante teórico e, na maioria dos casos, você deve preferir a API segura java null (caso seja lançada em mais 10 anos), mas NullPointerExceptioné uma subclasse de um Exception. Portanto, é uma forma Throwableque indica as condições que um aplicativo razoável pode querer capturar ( javadoc )! Para usar a primeira vantagem das exceções e separar o código de tratamento de erros do código 'regular' (de acordo com os criadores de Java ), é apropriado, quanto a mim, capturar NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }

    Perguntas podem surgir:

    Q. E se getPhotoDataSource()retornar nulo?
    R. Depende da lógica de negócios. Se não encontrar um álbum de fotos, não mostrarei fotos. E se appContext não for inicializado? A lógica de negócios desse método suporta isso. Se a mesma lógica for mais rigorosa, o lançamento de uma exceção faz parte da lógica de negócios e a verificação explícita de nulo deve ser usada (caso 3). A nova API Java Null-Safe se encaixa melhor aqui para especificar seletivamente o que implica e o que não implica que seja inicializado para ser à prova de falhas em caso de erros do programador.

    Q. Código redundante pode ser executado e recursos desnecessários podem ser capturados.
    R. Pode ocorrer se getPhotoByName()tentar abrir uma conexão com o banco de dados, criar PreparedStatemente usar o nome da pessoa como um parâmetro SQL finalmente. A abordagem para uma pergunta desconhecida fornece uma resposta desconhecida (caso 1) funciona aqui. Antes de pegar recursos, o método deve verificar os parâmetros e retornar o resultado 'desconhecido', se necessário.

    Q. Essa abordagem tem uma penalidade de desempenho devido à abertura de tentativa de fechamento.
    R. O software deve ser fácil de entender e modificar em primeiro lugar. Somente depois disso, é possível pensar em desempenho, e somente se necessário! e onde for necessário! ( fonte ) e muitos outros).

    PS. Essa abordagem será tão razoável quanto o código de tratamento de erros separado do princípio de código "regular" for razoável em algum lugar. Considere o próximo exemplo:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }

    PPS. Para aqueles que votam com menos votos (e não são tão rápidos em ler a documentação), gostaria de dizer que nunca vi uma exceção de ponteiro nulo (NPE) na minha vida. Mas essa possibilidade foi projetada intencionalmente pelos criadores de Java porque o NPE é uma subclasse de Exception. Temos um precedente na história Java quando ThreadDeathé um Errornão porque é realmente um erro de aplicação, mas apenas porque não tinha a intenção de ser pego! Quanto NPE se encaixa para ser um Errorque ThreadDeath! Mas não é.

  3. Marque 'Sem dados' apenas se a lógica de negócios implicar.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }

    e

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }

    Se appContext ou dataSource não for inicializado, o tempo de execução sem tratamento NullPointerException eliminará o encadeamento atual e será processado por Thread.defaultUncaughtExceptionHandler (para definir e usar seu criador de logs favorito ou outro mecanismo de notificação). Se não estiver definido, o ThreadGroup # uncaughtException imprimirá o rastreamento de pilha para o erro do sistema. Deve-se monitorar o log de erros do aplicativo e abrir o problema do Jira para cada exceção não tratada, que de fato é um erro do aplicativo. O programador deve corrigir o bug em algum lugar do material de inicialização.

Mykhaylo Adamovych
fonte
6
A captura NullPointerExceptione o retorno nullsão horríveis para depuração. Você acaba com o NPE mais tarde, e é realmente difícil descobrir o que era originalmente nulo.
Artbristol
23
Eu teria voto negativo se tivesse reputação. Não apenas nulo não é necessário, é um buraco no sistema de tipos. Atribuir uma árvore a uma lista é um erro de tipo porque as árvores não são valores do tipo Lista; pela mesma lógica, atribuir nulo deve ser um erro de tipo, porque nulo não é um valor do tipo Objeto ou qualquer tipo útil para esse assunto. Até o homem que inventou o nulo considera seu "erro de bilhão de dólares". A noção de "um valor que pode ser do tipo T OU nada" é de seu próprio tipo e deve ser representada como tal (por exemplo, Talvez <T> ou Opcional <T>).
Doval
2
A partir de "Talvez <T> ou Opcional <T>", você ainda precisa escrever um código como: if (maybeNull.hasValue()) {...}qual é a diferença if (maybeNull != null)) {...}?
Mykhaylo Adamovych
1
Desde "capturar NullPointerException e retornar null é horrível para depuração. Você acaba com o NPE mais tarde de qualquer maneira e é realmente difícil descobrir o que era originalmente nulo". Estou totalmente de acordo! Nesses casos, você deve escrever uma dúzia de instruções 'if' ou lançar o NPE se a lógica de negócios implicar dados no local ou usar um operador seguro para nulos no novo Java. Mas há casos em que não me importo com o passo exato que me dá nulo. Por exemplo, o cálculo de alguns valores para o usuário antes de aparecer na tela quando você espera que os dados estejam ausentes.
Mykhaylo Adamovych
3
@MykhayloAdamovych: O benefício Maybe<T>ou Optional<T>não está no caso em que o seu Tpode ser nulo, mas no caso em que nunca deve ser nulo. Se você possui um tipo que significa explicitamente "esse valor pode ser nulo - use com cautela" e usa e retorna esse tipo de forma consistente, sempre que vir um Tcódigo antigo simples no seu código, poderá assumir que ele nunca é nulo. (Claro, isso seria muito mais útil se executória pelo compilador.)
Chao
71

O Java 7 tem uma nova java.util.Objectsclasse de utilitário na qual existe um requireNonNull()método. Tudo o que isso faz é lançar a NullPointerExceptionse seu argumento for nulo, mas ele limpará um pouco o código. Exemplo:

Objects.requireNonNull(someObject);
someObject.doCalc();

O método é mais útil para verificar imediatamente antes de uma atribuição em um construtor, em que cada uso pode salvar três linhas de código:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

torna-se

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}
Raedwald
fonte
9
Na verdade, seu exemplo constitui inchaço no código: a primeira linha é supérflua porque o NPE seria lançado na segunda linha. ;-)
user1050755 31/07
1
Verdade. Um exemplo melhor seria se a segunda linha fosse doCalc(someObject).
Stuart Marcas
Depende. Se você é o autor de doCalc (), sugiro colocar a verificação no corpo desse método (se possível). E então você provavelmente chamará someObject.someMethod () onde novamente não há necessidade de verificar se há nulo. :-)
user1050755
Bem, se você não é o autor doCalc(), e ele não lança imediatamente o NPE quando recebe um valor nulo, você precisa verificar se há nulo e jogar o NPE você mesmo. É para isso que Objects.requireNonNull()serve.
Stuart Marcas
7
Não é apenas o inchaço do código. É melhor verificar antecipadamente do que na metade de um método que causa efeitos colaterais ou usa tempo / espaço.
Rob Grant
51

Por fim, a única maneira de resolver completamente esse problema é usando uma linguagem de programação diferente:

  • No Objective-C, você pode fazer o equivalente a invocar um método nil, e absolutamente nada acontecerá. Isso torna a maioria das verificações nulas desnecessárias, mas pode dificultar o diagnóstico de erros.
  • Em Nice , uma linguagem derivada de Java, existem duas versões de todos os tipos: uma versão potencialmente nula e uma versão não nula. Você pode invocar métodos apenas em tipos não nulos. Tipos potencialmente nulos podem ser convertidos em tipos não nulos através da verificação explícita de nulo. Isso facilita muito saber onde as verificações nulas são necessárias e onde não são.
Michael Borgwardt
fonte
Woah ... A resposta mais correta e sua resposta negativa, onde está a justiça? Em Java, nulo é sempre um valor válido. É o corolário de Tudo é um Objeto - Nulo é um Tudo (é claro que estamos ignorando os primitivos aqui, mas você entendeu). Pessoalmente, sou a favor da abordagem adotada por Nice, embora possamos fazê-la para que métodos possam ser chamados em tipos anuláveis ​​e promover NPEs a exceções verificadas. Isso teria de ser feito através de uma opção de compilador embora como ele iria quebrar todos os códigos existentes :(
CurtainDog
4
Eu não estou familiarizado com Nice, mas o Kotlin implementa a mesma idéia, tem tipos anuláveis ​​e não nulos incorporados ao sistema de tipos da linguagem. Muito mais conciso que Opcional ou Padrão de objeto nulo.
Mtsahakis 17/09/19
38

"Problema" comum em Java, de fato.

Primeiro, meus pensamentos sobre isso:

Considero que é ruim "comer" alguma coisa quando NULL foi passado onde NULL não é um valor válido. Se você não está saindo do método com algum tipo de erro, significa que nada deu errado no seu método, o que não é verdade. Então você provavelmente retorna nulo nesse caso, e no método de recebimento verifica novamente nulo, e nunca termina, e você termina com "se! = Nulo", etc.

Portanto, IMHO, null deve ser um erro crítico que impede a execução adicional (ou seja, onde null não é um valor válido).

O jeito que eu resolvo esse problema é o seguinte:

Primeiro, sigo esta convenção:

  1. Todos os métodos públicos / API sempre verificam seus argumentos como nulos
  2. Todos os métodos privados não verificam nulo, pois são métodos controlados (apenas deixe morrer com exceção de nullpointer, caso não tenha sido tratado acima)
  3. Os únicos outros métodos que não verificam nulo são os métodos utilitários. Eles são públicos, mas se você os chamar por algum motivo, saberá quais parâmetros você passa. É como tentar ferver água na chaleira sem fornecer água ...

E finalmente, no código, a primeira linha do método público é assim:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Observe que addParam () retorna self, para que você possa adicionar mais parâmetros para verificar.

O método validate()lançará verificado ValidationExceptionse algum dos parâmetros for nulo (marcado ou desmarcado é mais um problema de design / sabor, mas o meu ValidationExceptionestá marcado).

void validate() throws ValidationException;

A mensagem conterá o seguinte texto se, por exemplo, "planos" for nulo:

" O valor ilegal do argumento nulo é encontrado para o parâmetro [planos] "

Como você pode ver, o segundo valor no método addParam () (string) é necessário para a mensagem do usuário, porque você não pode detectar facilmente o nome da variável passada, mesmo com reflexão (mesmo que não seja o assunto desta postagem ...).

E sim, sabemos que além dessa linha, não encontraremos mais um valor nulo; portanto, apenas invocamos métodos com segurança nesses objetos.

Dessa forma, o código é limpo, fácil de manter e legível.

Oleg
fonte
3
Absolutamente. Os aplicativos que lançam apenas o erro e a falha são de qualidade superior porque não há dúvida de quando não estão funcionando. Os aplicativos que absorvem os erros, na melhor das hipóteses, degradam normalmente, mas geralmente não funcionam de maneiras difíceis de perceber e não são corrigidos. E quando o problema é percebido, eles são muito mais difíceis de depurar.
Paul Jackson
35

Ao fazer essa pergunta, você pode estar interessado em estratégias de tratamento de erros. O arquiteto da sua equipe deve decidir como trabalhar os erros. Existem várias maneiras de fazer isso:

  1. permita que as exceções se propagem - capture-as no 'loop principal' ou em alguma outra rotina de gerenciamento.

    • verifique se há condições de erro e lide com elas adequadamente

Claro, também dê uma olhada na Programação Orientada a Aspectos - eles têm maneiras legais de inserir if( o == null ) handleNull()seu código de bytes.

xtofl
fonte
35

Além de usar, assertvocê pode usar o seguinte:

if (someobject == null) {
    // Handle null here then move on.
}

Isso é um pouco melhor que:

if (someobject != null) {
    .....
    .....



    .....
}
fastcodejava
fonte
2
Mh, por que isso? Por favor, não sinto qualquer defensiva, eu só gostaria de saber mais sobre Java :)
Matthias Meid
9
@Udu Como regra geral, prefiro que a expressão em uma declaração if seja mais "positiva" do que negativa. Portanto, se eu visse if (!something) { x(); } else { y(); }, estaria inclinado a refatorá-lo como if (something) { y(); } else { x(); }(embora se possa argumentar que essa != nullé a opção mais positiva ...). Mais importante, porém, a parte importante do código não está envolvida em se {}você tem um nível a menos de indentação na maior parte do método. Não sei se esse foi o raciocínio de fastcodejava, mas seria o meu.
MatrixFrog
1
É isso que eu também faço. Mantém o código limpo em minha opinião.
precisa
34

Apenas nunca use null. Não permita.

Nas minhas classes, a maioria dos campos e variáveis ​​locais têm valores padrão não nulos e eu adiciono instruções de contrato (sempre ativadas) em todo o código para garantir que isso esteja sendo aplicado (já que é mais sucinto e mais expressivo do que permitir) aparecer como um NPE e depois resolver o número da linha etc.).

Depois de adotar essa prática, notei que os problemas pareciam se resolver. Você capturava as coisas muito mais cedo no processo de desenvolvimento por acidente e percebia que tinha um ponto fraco ... e mais importante .. ajuda a encapsular as preocupações dos diferentes módulos, diferentes módulos podem 'confiar' um no outro e não mais código com if = null elseconstruções!

Essa é uma programação defensiva e resulta em um código muito mais limpo a longo prazo. Sempre desinfete os dados, por exemplo, aqui, aplicando padrões rígidos, e os problemas desaparecem.

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

Os contratos são como mini-testes de unidade que estão sempre em execução, mesmo em produção, e quando as coisas falham, você sabe o porquê, em vez de um NPE aleatório, você precisa descobrir de alguma forma.

iangreen
fonte
por que isso seria reduzido? na minha experiência, isso é muito superior às outras abordagens, gostaria de saber por que não
ianpojman 31/10/12
Concordo que essa abordagem evita problemas relacionados a nulos, em vez de corrigi-los, salpicando verificações nulas de código em todos os lugares.
27413 ChrisBlom
7
O problema dessa abordagem é que, se o nome nunca for definido, ele terá o valor "<desconhecido>", que se comporta como um valor definido. Agora, digamos que eu precise verificar se o nome nunca foi definido (desconhecido), eu tenho que fazer uma comparação de string com o valor especial "<desconhecido>".
21413 Steve Kuo
1
Verdadeiro ponto positivo Steve. O que costumo fazer é ter esse valor como uma constante, por exemplo, estática pública final String UNSET = "__ unset" ... private String field = UNSET ... então private boolean isSet () {return UNSET.equals (field); }
ianpojman
IMHO, esta é uma implementação do Null Object Pattern com uma implementação do Opcional (Contrato). Como se comporta na classe de classe de persistência? Não considero aplicável nesse caso.
Kurapika
31

Guava, uma biblioteca principal muito útil do Google, possui uma API agradável e útil para evitar nulos. Acho UsingAndAvoidingNullExplained muito útil.

Conforme explicado no wiki:

Optional<T>é uma maneira de substituir uma referência T anulável por um valor não nulo. Um opcional pode conter uma referência T não nula (nesse caso, dizemos que a referência está "presente") ou pode não conter nada (nesse caso, dizemos que a referência está "ausente"). Nunca se diz que "contenha nulo".

Uso:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
Murat
fonte
@CodyGuldner Certo. Forneci uma citação relevante no link para fornecer mais contexto.
Murat Derya Özen
25

Esse é um problema muito comum para todos os desenvolvedores Java. Portanto, há suporte oficial no Java 8 para resolver esses problemas sem código desordenado.

O Java 8 foi introduzido java.util.Optional<T>. É um contêiner que pode ou não conter um valor não nulo. O Java 8 forneceu uma maneira mais segura de manipular um objeto cujo valor pode ser nulo em alguns casos. É inspirado nas idéias de Haskell e Scala .

Em poucas palavras, a classe Opcional inclui métodos para lidar explicitamente com os casos em que um valor está presente ou ausente. No entanto, a vantagem comparada às referências nulas é que a classe Opcional <T> força você a pensar no caso em que o valor não está presente. Como conseqüência, você pode evitar exceções não desejadas de ponteiro nulo.

No exemplo acima, temos uma fábrica de serviços domésticos que retorna um identificador para vários aparelhos disponíveis em casa. Mas esses serviços podem ou não estar disponíveis / funcionais; isso significa que pode resultar em uma NullPointerException. Em vez de adicionar um valor nuloif condição antes de usar qualquer serviço, envolva-a no <Serviço> Opcional.

ENVOLVER PARA A OPÇÃO <T>

Vamos considerar um método para obter uma referência de um serviço de uma fábrica. Em vez de retornar a referência de serviço, envolva-a com Opcional. Permite que o usuário da API saiba que o serviço retornado pode ou não estar disponível / funcional, usar defensivamente

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Como você vê, Optional.ofNullable()fornece uma maneira fácil de envolver a referência. Existem outras maneiras de obter a referência de Opcional, Optional.empty()&Optional.of() . Um para retornar um objeto vazio em vez de ajustar novamente nulo e o outro para quebrar um objeto não nulo, respectivamente.

ASSIM COMO EXATAMENTE AJUDA A EVITAR UMA VERIFICAÇÃO NULA?

Depois de agrupar um objeto de referência, o Opcional fornece muitos métodos úteis para chamar métodos em uma referência agrupada sem o NPE.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent chama o Consumidor especificado com uma referência, se for um valor não nulo. Caso contrário, não faz nada.

@FunctionalInterface
public interface Consumer<T>

Representa uma operação que aceita um único argumento de entrada e não retorna nenhum resultado. Diferentemente da maioria das outras interfaces funcionais, o Consumer deve operar por meio de efeitos colaterais. É tão limpo e fácil de entender. No exemplo de código acima, HomeService.switchOn(Service)é invocado se a referência de retenção Opcional for não nula.

Usamos o operador ternário frequentemente para verificar a condição nula e retornar um valor alternativo ou valor padrão. Opcional fornece outra maneira de lidar com a mesma condição sem verificar nulo. Optional.orElse (defaultObj) retorna defaultObj se o Opcional tiver um valor nulo. Vamos usar isso em nosso código de exemplo:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Agora HomeServices.get () faz a mesma coisa, mas de uma maneira melhor. Ele verifica se o serviço já foi inicializado ou não. Se for, retorne o mesmo ou crie um novo serviço Novo. <T> .orElse (T) opcional ajuda a retornar um valor padrão.

Finalmente, aqui está o nosso NPE, bem como o código nulo de verificação gratuita:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

A publicação completa é NPE e também código nulo para verificação de cheques ... Sério? .

Yogesh Devatraj
fonte
Há uma verificação nula no código acima - if(homeServices != null) {que pode ser alterado para homeServices.ifPresent(h -> //action);
KrishPrabakar
23

Eu gosto de artigos de Nat Pryce. Aqui estão os links:

Nos artigos, há também um link para um repositório Git para um Java Maybe Type que eu acho interessante, mas não acho que isso possa diminuir o inchaço do código de verificação. Depois de fazer algumas pesquisas na Internet, acho que ! = Inchaço de código nulo pode ser diminuído principalmente por um design cuidadoso.

Mr Palo
fonte
Michael Feathers escreveu um curto e texto interesthing sobre abordagens como o que você mencionou: manuelp.newsblur.com/site/424
ivan.aguirre
21

Eu tentei o NullObjectPatternmas para mim nem sempre é o melhor caminho a percorrer. Às vezes, quando uma "não ação" não é apropriada.

NullPointerExceptioné uma exceção de tempo de execução, que significa falha dos desenvolvedores e, com experiência suficiente, informa exatamente onde está o erro.

Agora a resposta:

Tente tornar todos os seus atributos e seus acessadores o mais privado possível ou evite expô-los aos clientes. Você pode ter os valores do argumento no construtor, é claro, mas reduzindo o escopo, você não deixa a classe cliente passar um valor inválido. Se você precisar modificar os valores, sempre poderá criar um novo object. Você verifica os valores no construtor apenas uma vez e, no restante dos métodos, pode ter quase certeza de que os valores não são nulos.

Obviamente, a experiência é a melhor maneira de entender e aplicar essa sugestão.

Byte!

OscarRyz
fonte
19

Provavelmente, a melhor alternativa para o Java 8 ou mais recente é usar a Optionalclasse

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

Isso é especialmente útil para longas cadeias de possíveis valores nulos. Exemplo:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Exemplo sobre como lançar exceção em null:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

O Java 7 introduziu o Objects.requireNonNullmétodo que pode ser útil quando algo deve ser verificado quanto à não-nulidade. Exemplo:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
Raghu K Nair
fonte
17

Posso responder de maneira mais geral!

Nós geralmente enfrentam esse problema quando os métodos de obter os parâmetros na forma como não se espera (mau chamada de método é culpa do programador). Por exemplo: você espera obter um objeto, em vez disso, obtém um nulo. Você espera obter uma String com pelo menos um caractere, em vez de uma String vazia ...

Portanto, não há diferença entre:

if(object == null){
   //you called my method badly!

}

ou

if(str.length() == 0){
   //you called my method badly again!
}

Ambos querem ter certeza de que recebemos parâmetros válidos antes de executar outras funções.

Conforme mencionado em algumas outras respostas, para evitar os problemas acima, você pode seguir o padrão Design por contrato . Por favor, consulte http://en.wikipedia.org/wiki/Design_by_contract .

Para implementar esse padrão em java, você pode usar anotações básicas de java como javax.annotation.NotNull ou usar bibliotecas mais sofisticadas como o Hibernate Validator .

Apenas uma amostra:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Agora você pode desenvolver com segurança a função principal do seu método sem precisar verificar os parâmetros de entrada, pois eles protegem seus métodos de parâmetros inesperados.

Você pode dar um passo adiante e garantir que apenas pojos válidos possam ser criados no seu aplicativo. (amostra do site do validador de hibernação)

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}
Alireza Fattahi
fonte
javax, por definição, não é "Java principal".
Tomas
17

Eu ignoro as respostas que sugerem o uso de objetos nulos em todas as situações. Esse padrão pode quebrar o contrato e enterrar os problemas cada vez mais fundo, em vez de resolvê-los, sem mencionar que o uso inadequado criará outra pilha de código padrão que exigirá manutenção futura.

Na realidade, se algo retornado de um método puder ser nulo e o código de chamada tiver que tomar uma decisão sobre isso, deve haver uma chamada anterior que garanta o estado.

Lembre-se também de que esse padrão de objeto nulo terá falta de memória se usado sem cuidado. Para isso - a instância de um NullObject deve ser compartilhada entre proprietários, e não uma instância de unigue para cada um deles.

Também não recomendaria usar esse padrão onde o tipo deve ser uma representação de tipo primitiva - como entidades matemáticas, que não são escalares: vetores, matrizes, números complexos e objetos POD (Plain Old Data), que devem conter o estado na forma de tipos internos Java. No último caso, você acabaria chamando métodos getter com resultados arbitrários. Por exemplo, o que um método NullPerson.getName () retornará?

Vale a pena considerar esses casos para evitar resultados absurdos.

luke1985
fonte
A solução com "hasBackground ()" tem uma desvantagem - não é segura para threads. Se você precisar chamar dois métodos em vez de um, precisará sincronizar a sequência inteira no ambiente multithread.
precisa saber é o seguinte
@pkalinow Você fez um exemplo artificial apenas para ressaltar que esta solução tem uma desvantagem. Se o código não for executado em aplicativos multithread, não haverá desvantagem. Eu poderia colocar para você provavelmente 90% do seu código que não é seguro para threads. Não estamos falando aqui sobre esse aspecto do código, mas sobre o padrão de design. E multithreading é um tópico por si só.
luke1985
É claro que em um aplicativo de thread único não é um problema. Fiz esse comentário porque às vezes é um problema.
22616 Pkalinow
@pkalinow Se você estudar esse tópico mais de perto, descobrirá que o padrão de design de Objeto Nulo não corrigirá os problemas de multithreading. Então é irrelevante. E, para ser sincero, encontrei lugares em que esse padrão se encaixaria perfeitamente, então minha resposta original está um pouco errada, na verdade.
luke1985
16
  1. Nunca inicialize variáveis ​​como nulas.
  2. Se (1) não for possível, inicialize todas as coleções e matrizes para esvaziar coleções / matrizes.

Faça isso em seu próprio código e você poderá evitar verificações! = Null.

Na maioria das vezes, as verificações nulas parecem proteger os loops de coleções ou matrizes; portanto, basta inicializá-las vazias, pois você não precisará de verificações nulas.

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

Há uma pequena sobrecarga nisso, mas vale a pena por código mais limpo e menos NullPointerExceptions.

Stuart Axon
fonte
3
+1 Isso eu concordo. Você nunca deve retornar objetos parcialmente inicializados. O código relacionado ao Jaxb e o código do bean não são necessários para isso. É uma prática ruim. Todas as coleções devem ser inicializadas e todos os objetos devem existir sem (idealmente) nenhuma referência nula. Considere um objeto que tenha uma coleção. Verificar se o objeto não é nulo, se a coleção não é nula e se a coleção não contém objetos nulos é irracional e tolo.
ggb667
15

Este é o erro mais comum ocorrido para a maioria dos desenvolvedores.

Temos várias maneiras de lidar com isso.

Abordagem 1:

org.apache.commons.lang.Validate //using apache framework

notNull (objeto Objeto, mensagem String)

Abordagem 2:

if(someObject!=null){ // simply checking against null
}

Abordagem 3:

@isNull @Nullable  // using annotation based validation

Abordagem 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}
Sireesh Yarlagadda
fonte
De Anúncios. 4. Não é muito útil - quando você verifica se um ponteiro é nulo, provavelmente deseja chamar um método nele. Chamar um método em null fornece o mesmo comportamento - NullPointerException.
precisa saber é o seguinte
11
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
tltester
fonte
2
O que há de errado com esse método, acho que o @tltester só quer dar um valor padrão se for nulo, o que faz sentido.
Sawyer
1
Há tal método um em Apache commons-lang: ObjectUtils.defaultIfNull(). Há uma mais geral: ObjectUtils.firstNonNull()que pode ser usado para implementar uma estratégia degradante:firstNonNull(bestChoice, secondBest, thirdBest, fallBack);
ivant