4420 votes

Comment éviter de vérifier les valeurs nulles en Java?

J'utilise x != null pour éviter NullPointerException. Y a-t-il une alternative?

if (x != null) {
    // ...
}

144 votes

@Shervin Encourager les valeurs nulles rend le code moins compréhensible et moins fiable.

85 votes

Ne pas utiliser null est supérieur à la plupart des autres suggestions ici. Lancez des exceptions, ne renvoyez pas ou n'autorisez pas les nulls. En passant - le mot clé 'assert' est inutile, car il est désactivé par défaut. Utilisez un mécanisme d'échec toujours activé.

0 votes

Vous pouvez créer une classe qui vérifiera les valeurs nulles ou les objets nuls. Cela vous aidera à améliorer la réutilisabilité.. stackoverflow.com/a/16833309/1490962

2863voto

cletus Points 276888

Cela me semble être un problème assez courant que les développeurs juniors à intermédiaires ont tendance à rencontrer à un moment donné : ils ne connaissent pas ou ne font pas confiance aux contrats auxquels ils participent et vérifient défensivement les nulls. De plus, lorsqu'ils écrivent leur propre code, ils ont tendance à renvoyer des nulls pour indiquer quelque chose nécessitant ainsi à l'appelant de vérifier les nulls.

Pour le dire autrement, il y a deux cas où la vérification de null se pose :

  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. À partir de Java 1.7, vous pouvez utiliser Objects.requireNonNull(foo). (Si vous êtes coincé avec une version précédente, alors les asserttions peuvent être une bonne alternative.)

L'utilisation "correcte" de cette méthode serait comme ci-dessous. La méthode renvoie l'objet passé en elle et lance une NullPointerException si l'objet est null. Cela signifie que la valeur retournée est toujours non nulle. La méthode est principalement destinée à valider les paramètres.

public Foo(Bar bar) {
    this.bar = Objects.requireNonNull(bar);
}

Elle peut également être utilisée comme une assertion puisqu'elle lance une exception si l'objet est null. Dans les deux utilisations, un message peut être ajouté qui sera affiché dans l'exception. Ci-dessous, on l'utilise comme une assertion et en fournissant un message.

Objects.requireNonNull(someobject, "si someobject est nul alors quelque chose ne va pas");
someobject.doCalc();

En général, lancer une exception spécifique comme NullPointerException lorsque la valeur est null mais ne devrait pas l'être est favorable à lancer une exception plus générale comme AssertionError. C'est l'approche mise en œuvre par la bibliothèque Java ; préférant NullPointerException à IllegalArgumentException lorsque l'argument ne peut pas être null.

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

S'il s'agit du code que vous contrôlez, cependant (ce qui est souvent le cas), c'est une autre histoire. Évitez d'utiliser les nulls comme réponse. Avec les méthodes qui renvoient des collections, c'est facile : renvoyez des collections vides (ou des tableaux) au lieu des nulls la plupart du temps.

Avec les non-collections, cela peut ê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 en ligne de commande pour quelque chose. Vous pourriez faire le contrat qu'il renvoie null s'il n'y a pas d'action appropriée. Cela conduit à la vérification de null dont vous parlez.

Une solution alternative est de ne jamais renvoyer null et d'utiliser plutôt le modèle d'objet nul:

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

  public Action findAction(String userInput) {
    // ...
    if ( /* nous ne pouvons trouver aucune action */ ) {
      return DO_NOTHING;
    }
  }
}

Comparez :

Parser parser = ParserFactory.getParser();
if (parser == null) {
  // et maintenant ?
  // ceci serait un exemple où null n'est pas (ou ne devrait pas être) une réponse valide
}
Action action = parser.findAction(someInput);
if (action == null) {
  // ne rien faire
} else {
  action.doSomething();
}

à

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

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

Cela dit, il est peut-être tout à fait approprié que la méthode findAction() lance une Exception avec un message d'erreur significatif -- surtout dans ce cas où vous vous fiez à l'entrée utilisateur. Ce serait beaucoup mieux pour la méthode findAction de lancer une Exception que pour la méthode appelante de s'arrêter avec une simple NullPointerException sans explication.

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

Ou si vous trouvez le mécanisme try/catch trop laid, au lieu de Ne Rien Faire, votre action par défaut devrait fournir un retour d'information à l'utilisateur.

public Action findAction(final String userInput) {
    /* Code pour renvoyer l'Action demandée si trouvée */
    return new Action() {
        public void doSomething() {
            userConsole.err("Action non trouvée : " + userInput);
        }
    }
}

8 votes

Bonne réponse, en particulier en mentionnant les assertions. Je viens de C et je les utilise constamment. En ce qui concerne les objets Null, ce ne sont PAS une solution miracle. J'ai souvent passé des heures à déboguer du code qui s'est avéré être un objet Null utilisé, ne faisant rien (ou retournant ses valeurs par défaut) alors qu'une NPE aurait indiqué le problème de manière beaucoup plus claire. Ainsi, vous devrez effectuer une "Vérification de l'objet Null" au lieu d'une vérification de null, et vous n'avez rien gagné, en fait, vous avez perdu en clarté. De nos jours, si je ne peux pas éviter null, je le documente simplement de manière claire et c'est tout.

0 votes

Écrire this.bar = Objects.requireNonNull(bar); est un bon usage de cette méthode, car vous n'êtes pas sur le point d'utiliser this.bar. De cette manière, vous obtenez l'exception lorsqu'un objet nul invalide est passé, plutôt que lorsque vous essayez de l'utiliser. Cependant, il est souvent utilisé de cette façon: Objects.requireNonNull(getThing()).doThingStuff(); Ici, l'appel à Objects.requireNonNull() ne change pas du tout le comportement, il lance le NPE de toute façon. Il est préférable de l'utiliser lorsque vous sauvegardez un objet pour une utilisation ultérieure. Dans mon deuxième exemple, l'exception est utile. Elle aide à traquer le bug.

2 votes

Devrions-nous suggérer d'utiliser des exceptions pour le flux de contrôle? Bien que ce soit techniquement une solution qui fonctionne, les programmeurs devraient se souvenir que les exceptions ne devraient être utilisées que pour attraper des situations inattendues. Lors de la validation de l'entrée d'un utilisateur, une entrée invalide ne devrait idéalement pas entraîner le déclenchement d'exceptions.

716voto

Luca Molteni Points 2328

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

Fondamentalement, vous avez @Nullable et @NotNull.

Vous pouvez les utiliser dans une méthode et des 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 compilerait pas (dans IntelliJ IDEA).

Lorsque vous utilisez la première fonction helloWorld() dans une autre partie du 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 la vérification est inutile, car la fonction helloWorld() ne renverra jamais null.

Utilisation d'un paramètre

void someMethod(@NotNull someParameter) { }

si vous écrivez quelque chose comme :

someMethod(null);

Ceci ne compilerait pas.

Dernier exemple utilisant @Nullable

@Nullable iWantToDestroyEverything() { return null; }

En faisant cela

iWantToDestroyEverything().something();

Et vous pouvez être sûr que cela ne se produira pas. :)

C'est un moyen utile de permettre au compilateur de vérifier quelque chose de plus que ce qu'il fait habituellement et de renforcer vos contrats. Malheureusement, ce n'est pas pris en charge par tous les compilateurs.

Dans IntelliJ IDEA 10.5 et plus, ils ont ajouté la prise en charge de toute autre implémentation de @Nullable @NotNull.

Voir l'article de blog Annotations @Nullable/@NotNull plus flexibles et configurables.

134 votes

@NotNull, @Nullable et d'autres annotations de nullité font partie de JSR 305. Vous pouvez également les utiliser pour détecter des problèmes potentiels avec des outils comme FindBugs.

37 votes

Je trouve étrangement agaçant que les interfaces @NotNull et @Nullable se trouvent dans le package com.sun.istack.internal. (Je suppose que j'associe com.sun à des avertissements concernant l'utilisation d'une API propriétaire.)

22 votes

La portabilité du code devient nulle avec JetBrains. Je réfléchirais à deux fois avant de m'attacher au niveau de l'IDE. Comme l'a dit Jacek S, ils font de toute façon partie de la JSR, que je pensais être la JSR303 d'ailleurs.

353voto

myplacedk Points 1237

Si les valeurs nulles ne sont pas autorisées

Si votre méthode est appelée de l'extérieur, 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 n'est pas nul.

S'il s'agit d'une méthode interne (qui ne fait pas partie d'une API), contentez-vous de documenter le fait 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 se contente de transmettre la valeur, et que la méthode suivante la transmet à son tour, cela peut poser problème. Dans ce cas, vous voudrez peut-être vérifier l'argument comme décrit ci-dessus.

Si la valeur nulle est autorisée

Cela dépend vraiment. Je trouve que j'ai souvent recours à quelque chose comme ça :

if (object == null) {
  // quelque chose
} else {
  // quelque chose d'autre
}

Je fais donc une bifurcation, et je fais deux choses complètement différentes. Il n'y a pas de morceau de code laid, car j'ai vraiment besoin de faire deux choses différentes en fonction des données. Par exemple, dois-je travailler sur l'entrée, ou dois-je calculer une bonne valeur par défaut ?


C'est en fait rare que j'utilise l'idiome "if (object != null && ...".

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

103 votes

Quel est l'intérêt de lancer une IllegalArgumentException ? Je pense qu'une NullPointerException serait plus claire, et cela serait également lancé si vous ne faites pas la vérification de null vous-même. Je préfère utiliser assert ou rien du tout.

25 votes

Il est peu probable que toute autre valeur que null soit acceptable. Vous pourriez avoir IllegalArgumentException, OutOfRangeException etc etc. Parfois, cela a du sens. D'autres fois, vous finissez par créer beaucoup de classes d'exception qui n'ajoutent aucune valeur, puis vous utilisez simplement IllegalArgumentException. Il n'a aucun sens d'avoir une exception pour une saisie nulle, et une autre pour tout le reste.

7 votes

Oui, je suis d'accord avec le principe du "fail-fast", mais dans l'exemple donné ci-dessus, la valeur n'est pas transmise, mais c'est l'objet sur lequel une méthode doit être appelée. Donc cela échoue tout aussi rapidement, et ajouter une vérification de nulle juste pour lancer une exception qui serait de toute façon lancée au même moment et au même endroit ne semble pas faciliter le débogage.

259voto

erickson Points 127945

Wow, je déteste presque ajouter une autre réponse alors que nous avons 57 façons différentes de recommander le NullObject pattern, mais je pense que certaines personnes intéressées par cette question pourraient aimer savoir qu'il y a une proposition sur la table pour Java 7 pour ajouter la "gestion sûre du null" - 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 dé-référencer uniquement l'identifiant de gauche s'il n'est pas nul, sinon évaluer le reste de l'expression comme null. Certaines personnes, comme le membre de Java Posse Dick Wall et les votants à Devoxx adorent vraiment cette proposition, mais il y a aussi de l'opposition, parce qu'elle encouragerait en réalité une utilisation plus fréquente de null comme valeur sentinelle.


Mise à jour: Une proposition officielle pour un opérateur null-safe dans Java 7 a été soumise dans le cadre du Projet Coin. La syntaxe est un peu différente de l'exemple ci-dessus, mais c'est la même idée.


Mise à jour: La proposition d'opérateur null-safe n'a pas été retenue dans le Projet Coin. Vous ne verrez donc pas cette syntaxe dans Java 7.

23 votes

Je pense que c'est faux. Il devrait y avoir un moyen de spécifier qu'une variable donnée est TOUJOURS non nulle.

7 votes

Mise à jour : la proposition ne se fera pas en Java7. Voir blogs.sun.com/darcy/entry/project_coin_final_five .

2 votes

Mais sur la même page, consultez le lien vers types.cs.washington.edu/jsr308 pour des informations sur l'utilisation du cadre Checker pour ajouter les annotations de type @Nullable et @NonNull en Java 7.

214voto

thSoft Points 5513

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

Vous pouvez configurer votre IDE pour vous avertir des accès potentiels à null. Par exemple, dans Eclipse, consultez Préférences > Java > Compilateur > Erreurs/Avertissements/Analyse null.

Si les valeurs indéfinies sont autorisées :

Si vous souhaitez définir une nouvelle API où les valeurs indéfinies ont du sens, utilisez le Pattern Option (peut être familier aux langages fonctionnels). Il présente les avantages suivants :

  • Il est explicitement déclaré dans l'API si une entrée ou une sortie existe ou non.
  • Le compilateur vous oblige à gérer le cas "indéfini".
  • Option est un monade, donc il n'est pas nécessaire d'effectuer des vérifications verbeuses de null, il suffit d'utiliser map/foreach/getOrElse ou un combinateur similaire pour utiliser en toute sécurité la valeur (exemple).

Java 8 dispose d'une classe intégrée Optional (recommandée) ; pour les versions antérieures, il existe des alternatives de bibliothèque, par exemple l'Optionnel Guava de Guava ou l'Option de FunctionalJava. Mais comme de nombreux motifs de style fonctionnel, l'utilisation de l'Option en Java (même 8) entraîne une certaine quantité de code d'amorçage, que vous pouvez réduire en utilisant un langage JVM moins verbeux, par exemple Scala ou Xtend.

Si vous devez interagir avec une API qui peut renvoyer des valeurs nulles, vous ne pouvez pas faire grand-chose en Java. Xtend et Groovy disposent de l'opérateur Elvis ?: et de l'opérateur de déréférencement null-sûr ?., mais notez que cela renvoie null en cas de référence nulle, reportant ainsi le traitement approprié de null.

24 votes

En effet, le modèle Option est génial. Certains équivalents en Java existent. Guava contient une version limitée de cela appelée Optionnelle qui exclut la plupart des fonctionnalités fonctionnelles. En Haskell, ce modèle est appelé Peut-être.

0 votes

@Luca Molteni: Tu as tout à fait raison, j'ai déjà commenté sur le post pour demander de l'étendre. :)

11 votes

Une classe Optionnelle sera disponible en Java 8

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X