Question Éviter! = Déclarations nulles


j'utilise object != null beaucoup à éviter NullPointerException.

Y a-t-il une bonne alternative à cela?

Par exemple:

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

Cela évite un NullPointerException, quand on ne sait pas si l'objet est null ou pas.

Notez que la réponse acceptée peut être obsolète, voir https://stackoverflow.com/a/2386013/12943 pour une approche plus récente.


3548


origine


Réponses:


Pour moi, cela ressemble à un problème assez commun que les développeurs juniors à intermédiaires ont tendance à rencontrer à un moment donné: ils ne savent pas ou ne font pas confiance aux contrats auxquels ils participent et vérifient de manière défensive les nullités. En outre, lors de l'écriture de leur propre code, ils ont tendance à se fier à des valeurs nulles pour indiquer quelque chose, ce qui oblige l'appelant à rechercher les valeurs nulles.

Pour le dire autrement, il y a deux cas où la vérification nulle arrive:

  1. Où null est une réponse valide en termes de contrat; et

  2. Où ce n'est pas une réponse valide.

(2) est facile. Soit utiliser assert déclarations (assertions) ou permettre l'échec (par exemple, NullPointerException). Les assertions sont une fonctionnalité Java très sous-utilisée qui a été ajoutée en version 1.4. La syntaxe est:

assert <condition>

ou

assert <condition> : <object>

<condition> est une expression booléenne et <object> est un objet dont toString() La sortie de la méthode sera incluse dans l'erreur.

Un assert déclaration jette un Error (AssertionError) si la condition n'est pas vraie. Par défaut, Java ignore les assertions. Vous pouvez activer les assertions en passant l'option -ea à la JVM. Vous pouvez activer et désactiver les assertions pour des classes et des packages individuels. Cela signifie que vous pouvez valider le code avec les assertions lors du développement et du test, et les désactiver dans un environnement de production, bien que mes tests n'aient montré aucun impact sur les performances des assertions.

Ne pas utiliser d'assertions dans ce cas est OK car le code échouera, ce qui arrivera si vous utilisez des assertions. La seule différence est qu'avec des affirmations, cela pourrait arriver plus tôt, d'une manière plus significative et éventuellement avec des informations supplémentaires, ce qui peut vous aider à comprendre pourquoi cela est arrivé si vous ne l'attendiez pas.

(1) est un peu plus difficile. Si vous n'avez aucun contrôle sur le code que vous appelez, vous êtes bloqué. Si null est une réponse valide, vous devez le vérifier.

Si c'est le code que vous contrôlez, cependant (et c'est souvent le cas), alors c'est une histoire différente. Évitez d'utiliser des valeurs NULL comme réponse. Avec des méthodes qui retournent des collections, c'est facile: renvoyez des collections vides (ou des tableaux) au lieu de null à peu près tout le temps.

Avec des non-collections, cela pourrait être plus difficile. Considérez ceci comme un exemple: si vous avez ces interfaces:

public interface Action {
  void doSomething();
}

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

où Parser prend une entrée utilisateur brute et trouve quelque chose à faire, peut-être si vous implémentez une interface de ligne de commande pour quelque chose. Vous pouvez maintenant faire en sorte que le contrat renvoie null s'il n'y a pas d'action appropriée. Cela mène la vérification nulle dont vous parlez.

Une solution alternative est de ne jamais retourner null et à la place utiliser le Modèle d'objet nul:

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;
    }
  }
}

Comparer:

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();
}

à

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

ce qui est un design bien meilleur car il conduit à un code plus concis.

Cela dit, il est peut-être tout à fait approprié pour la méthode findAction () de lancer une exception avec un message d'erreur significatif - en particulier dans ce cas où vous comptez sur l'entrée de l'utilisateur. Il serait beaucoup mieux pour la méthode findAction de lancer une exception que pour la méthode d'appel de sauter avec une simple exception NullPointerException sans explication.

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

Ou si vous pensez que le mécanisme try / catch est trop moche, plutôt que de ne rien faire, votre action par défaut devrait fournir un retour à l'utilisateur.

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);
        }
    }
}

2389



Si vous utilisez (ou prévoyez d'utiliser) un IDE Java comme JetBrains IntelliJ IDEA, Eclipse ou Netbeans ou un outil comme findbugs alors vous pouvez utiliser des annotations pour résoudre ce problème.

Fondamentalement, vous avez @Nullable et @NotNull.

Vous pouvez utiliser dans la méthode et les paramètres, comme ceci:

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

ou

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

Le deuxième exemple ne compilera pas (dans IntelliJ IDEA).

Lorsque vous utilisez le premier helloWorld() fonctionner dans un autre morceau de code:

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

Maintenant, le compilateur IntelliJ IDEA vous dira que le contrôle est inutile, puisque le helloWorld() la fonction ne reviendra pas null, déjà.

En utilisant le paramètre

void someMethod(@NotNull someParameter) { }

si vous écrivez quelque chose comme:

someMethod(null);

Cela ne compilera pas.

Dernier exemple en utilisant @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Ce faisant

iWantToDestroyEverything().something();

Et vous pouvez être sûr que cela n'arrivera pas. :)

C'est une bonne façon de laisser le compilateur vérifier quelque chose de plus que d'habitude et de renforcer vos contrats pour qu'ils soient plus forts. Malheureusement, ce n'est pas supporté par tous les compilateurs.

Dans IntelliJ IDEA 10.5 et plus, ils ont ajouté un support pour tout autre @Nullable  @NotNull implémentations.

Voir le blog Annotations @ Nullable / @ NotNull plus flexibles et configurables.


516



Si les valeurs nulles ne sont pas autorisées

Si votre méthode est appelée en externe, commencez par quelque chose comme ceci:

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

Ensuite, dans le reste de cette méthode, vous saurez que object est non nulle.

S'il s'agit d'une méthode interne (ne faisant pas partie d'une API), documentez simplement qu'elle ne peut pas être nulle, et c'est tout.

Exemple:

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

Cependant, si votre méthode ne fait que passer la valeur, et que la méthode suivante la transmet, etc., cela pourrait poser problème. Dans ce cas, vous pouvez vouloir vérifier l'argument comme ci-dessus.

Si la valeur null est autorisée

Cela dépend vraiment. Si je trouve que je fais souvent quelque chose comme ceci:

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

Donc je branche, et fais deux choses complètement différentes. Il n'y a pas d'extrait de code moche, parce que j'ai vraiment besoin de faire deux choses différentes en fonction des données. Par exemple, devrais-je travailler sur l'entrée, ou devrais-je calculer une bonne valeur par défaut?


C'est en fait rare pour moi d'utiliser l'idiome "if (object != null && ...".

Il peut être plus facile de vous donner des exemples, si vous montrez des exemples d'où vous utilisez généralement l'idiome.


282



Wow, je déteste presque ajouter une autre réponse quand nous avons 57 façons différentes de recommander le NullObject pattern, mais je pense que certaines personnes intéressées par cette question aimeraient savoir qu'il y a une proposition sur la table pour Java 7 à ajouter "manipulation sans risque"-une syntaxe simplifiée pour la logique if-not-equal-null.

L'exemple donné par Alex Miller ressemble à ceci:

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

le ?. signifie uniquement ne pas référencer l'identificateur de gauche s'il n'est pas null, sinon évaluer le reste de l'expression comme null. Certaines personnes, comme le membre de Dick Posse Dick Wall et le électeurs de Devoxx J'adore vraiment cette proposition, mais il y a aussi de l'opposition, au motif que cela encouragera null comme une valeur sentinelle.


Mettre à jour: Un proposition officielle pour un opérateur null-safe dans Java 7 a été soumis sous Project Coin. La syntaxe est un peu différente de l'exemple ci-dessus, mais c'est la même notion.


Mettre à jour: La proposition d'opérateur null-safe ne l'a pas fait dans Project Coin. Donc, vous ne verrez pas cette syntaxe dans Java 7.


215



Si des valeurs indéfinies ne sont pas autorisées:

Vous pouvez configurer votre EDI pour vous prévenir du déréférencement nul potentiel. Par exemple. dans Eclipse, voir Préférences> Java> Compilateur> Erreurs / Avertissements / Analyse nulle.

Si des valeurs indéfinies sont autorisées:

Si vous voulez définir une nouvelle API où des valeurs indéfinies ont du sens, Utilisez le Modèle d'option (peut être familier des langages fonctionnels). Il a les avantages suivants:

  • Il est indiqué explicitement dans l'API si une entrée ou une sortie existe ou non.
  • Le compilateur vous force à gérer le cas "indéfini".
  • L'option est une monade, donc il n'y a pas besoin de vérification verbeuse verbeuse, utilisez simplement map / foreach / getOrElse ou un combinateur similaire pour utiliser la valeur en toute sécurité (Exemple).

Java 8 a un intégré Optional classe (recommandé); pour les versions antérieures, il existe des alternatives de bibliothèque, par exemple Goyavede Optional ou FunctionalJavade Option. Mais comme de nombreux modèles de style fonctionnel, l'utilisation de l'option Java (même 8) donne de bons résultats, que vous pouvez réduire en utilisant un langage JVM moins bavard, par ex. Scala ou Xtend.

Si vous devez gérer une API qui pourrait renvoyer des valeurs nulles, vous ne pouvez pas faire grand-chose en Java. Xtend et Groovy ont le Elvis opérateur  ?: et le opérateur de déréférencement null-safe  ?., mais notez que cela renvoie null dans le cas d'une référence nulle, de sorte qu'il "retarde" juste le traitement correct de null.


177



Seulement pour cette situation -

Ne pas vérifier si une variable est null avant d'appeler une méthode equals (exemple de comparaison de chaîne ci-dessous):

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

résultera en un NullPointerException si foo n'existe pas.

Vous pouvez éviter cela si vous comparez Strings aime ceci:

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

160



Avec Java 8 vient le nouveau java.util.Optional classe qui résout sans doute une partie du problème. On peut au moins dire que cela améliore la lisibilité du code, et dans le cas des API publiques, le contrat de l'API est plus clair pour le développeur du client.

Ils travaillent comme ça:

Un objet optionnel pour un type donné (Fruit) est créé en tant que type de retour d'une méthode. Il peut être vide ou contenir un Fruit objet:

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();
}

Maintenant, regardez ce code où nous cherchons une liste de Fruit (fruits) pour une instance de Fruit donnée:

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

Vous pouvez utiliser le map() opérateur pour effectuer un calcul sur - ou extraire une valeur de - un objet optionnel. orElse() vous permet de fournir une solution de repli pour les valeurs manquantes.

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

Bien sûr, la vérification de la valeur nulle / vide est toujours nécessaire, mais au moins le développeur est conscient que la valeur peut être vide et le risque d'oubli à vérifier est limité.

Dans une API construite à partir de zéro en utilisant Optional chaque fois qu'une valeur de retour peut être vide, et retourner un objet brut seulement quand il ne peut pas être null (convention), le code client peut abandonner les vérifications nuls sur des valeurs de retour d'objet simples ...

Bien sûr Optional pourrait également être utilisé comme argument de méthode, peut-être un meilleur moyen d'indiquer des arguments facultatifs que 5 ou 10 méthodes de surcharge dans certains cas.

Optional offre d'autres méthodes pratiques, telles que orElse qui permettent l'utilisation d'une valeur par défaut, et ifPresent ça marche avec expressions lambda.

Je vous invite à lire cet article (ma source principale pour écrire cette réponse) dans lequel le NullPointerException (et en général null pointeur) problématique ainsi que la solution (partielle) apportée par Optional sont bien expliqués: Objets facultatifs Java.


135



En fonction du type d'objets que vous vérifiez, vous pourrez peut-être utiliser certaines classes dans les communs apache comme: apache commun lang et apache commons collections

Exemple:

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

ou (en fonction de ce que vous devez vérifier):

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

La classe StringUtils n'est qu'une parmi beaucoup d'autres; il y a pas mal de bonnes classes dans les communs qui ne font aucune manipulation sécuritaire.

Voici un exemple de la façon dont vous pouvez utiliser la validation nulle dans JAVA lorsque vous incluez la bibliothèque 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);
}

Et si vous utilisez Spring, Spring a également la même fonctionnalité dans son paquet, voir library (spring-2.4.6.jar)

Exemple sur comment utiliser cette classe statique à partir du printemps (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

113



  • Si vous considérez qu'un objet ne doit pas être nul (ou s'il s'agit d'un bogue), utilisez un assert.
  • Si votre méthode n'accepte pas les paramètres null, dites-le dans le javadoc et utilisez un assert.

Vous devez vérifier object! = Null seulement si vous voulez gérer le cas où l'objet peut être nul ...

Il existe une proposition pour ajouter de nouvelles annotations dans Java7 pour aider avec les paramètres null / notnull: http://tech.puredanger.com/java7/#jsr308


87