Question

J'utilise object != null beaucoup pour éviter < => .

Existe-t-il une bonne alternative à cela?

Par exemple:

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

Ceci évite un NullPointerException, lorsqu'il est inconnu si l'objet est null ou non.

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

Était-ce utile?

La solution

Pour moi, cela ressemble à un problème assez commun auquel les développeurs débutants à intermédiaires ont tendance à être confrontés à un moment donné: ils ne savent pas ou ne font pas confiance aux contrats auxquels ils participent et sur-vérifient de manière défensive la nullité. De plus, lorsqu’ils écrivent leur propre code, ils ont tendance à compter sur le renvoi de valeurs NULL pour indiquer quelque chose, ce qui oblige l’appelant à rechercher les valeurs NULL.

En d'autres termes, il existe deux cas de vérification de la valeur Null:

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

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

(2) est facile. Utilisez des instructions assert (assertions) ou autorisez un échec (par exemple, NullPointerException ). Les assertions sont une fonctionnalité Java très sous-utilisée ajoutée dans la version 1.4. La syntaxe est la suivante:

assert <condition>

ou

assert <condition> : <object>

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

Une instruction Error renvoie un AssertionError (-ea) si la condition n'est pas vraie. Par défaut, Java ignore les assertions. Vous pouvez activer les assertions en transmettant l'option <=> à la machine virtuelle Java. Vous pouvez activer et désactiver des assertions pour des classes et des packages individuels. Cela signifie que vous pouvez valider le code avec les assertions lors du développement et des tests, et les désactiver dans un environnement de production, bien que mes tests n'aient montré aucun impact sur les performances des assertions.

Dans ce cas, il est correct de ne pas utiliser d'assertions, car le code échouera simplement, ce qui se produira si vous utilisez des assertions. La seule différence est qu’avec les affirmations, cela peut arriver plus tôt, de manière plus significative et éventuellement avec des informations supplémentaires, ce qui peut vous aider à comprendre pourquoi cela s’est produit si vous ne vous y 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 la valeur null est une réponse valide, vous devez la vérifier.

Si c'est le code que vous contrôlez cependant (et c'est souvent le cas), c'est une autre histoire. Évitez d’utiliser des valeurs NULL en réponse. Avec des méthodes qui renvoient des collections, rien de plus simple: renvoyez des collections vides (ou des tableaux) au lieu de NULL presque tout le temps.

Avec les 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. Maintenant, vous pouvez indiquer au contrat qu'il renvoie la valeur null s'il n'y a pas d'action appropriée. Cela mène à la vérification de nullité dont vous parlez.

Une autre solution consiste à ne jamais renvoyer null et à utiliser le modèle d'objet Null :

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

Comparez:

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

qui est une conception bien meilleure car elle conduit à un code plus concis.

Cela dit, il est peut-être tout à fait approprié que la méthode findAction () lève une exception avec un message d'erreur significatif, en particulier dans le cas où vous vous fiez à la saisie de l'utilisateur. Il serait bien mieux que la méthode findAction lève une exception plutôt que que la méthode appelante explose 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 laid, plutôt que de ne rien faire, votre action par défaut devrait fournir des informations à 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);
        }
    }
}

Autres conseils

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

En gros, vous avez @Nullable et @NotNull.

Vous pouvez utiliser 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 la première helloWorld() fonction 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 la vérification est inutile, car la null fonction ne renverra jamais <=>.

Utilisation du paramètre

void someMethod(@NotNull someParameter) { }

si vous écrivez quelque chose comme:

someMethod(null);

Ceci ne compilera pas.

Dernier exemple utilisant <=>

@Nullable iWantToDestroyEverything() { return null; }

Faire ceci

iWantToDestroyEverything().something();

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

C'est un bon moyen de laisser le compilateur vérifier quelque chose de plus que d'habitude et de renforcer vos contrats. Malheureusement, tous les compilateurs ne le supportent pas.

Dans IntelliJ IDEA 10.5 et versions ultérieures, ils ont ajouté la prise en charge de toute autre implémentation <=> <=>.

Voir le blog Plus annotations @ Nullable / @ NotNull flexibles et configurables .

Si les valeurs NULL 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 n'est pas null.

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

Exemple:

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

Cependant, si votre méthode ne fait que transmettre la valeur, et que la méthode suivante la transmet, etc., cela pourrait devenir problématique. Dans ce cas, vous pouvez vérifier l’argument comme ci-dessus.

Si null est autorisé

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

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

Alors je branche, et fais deux choses complètement différentes. Il n'y a pas d'extrait de code laid, car je dois vraiment 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?

En fait, il est rare que j'utilise l'idiome & "; if (object != null && ... &".

.

Il sera peut-être plus facile de vous donner des exemples si vous montrez des exemples d'utilisation typique de cet idiome.

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 voudront peut-être savoir qu’une proposition a été faite pour Java 7 pour ajouter & "; gestion null-safe &"; & 8212; 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();  
}  

Les ?. moyens ne font que renvoyer l'identifiant de gauche s'il n'est pas nul, sinon, évaluer le reste de l'expression comme null. Certaines personnes, comme Dick Wall, membre de Java Posse, et le Les électeurs de Devoxx adorent cette proposition, mais il existe également une opposition, car elle encouragera en réalité une utilisation accrue de <=> comme valeur sentinelle.

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

Mise à jour : la proposition de l'opérateur null-safe n’a pas été intégrée à Project Coin. Donc, vous ne verrez pas cette syntaxe dans Java 7.

Si les valeurs non définies ne sont pas autorisées:

Vous pouvez configurer votre IDE pour vous avertir du possible déréférencement Null. Par exemple. dans Eclipse, voir Préférences > Java & Gt; Compilateur & Gt; Erreurs / Avertissements / Analyse nulle .

Si des valeurs non définies sont autorisées:

Si vous souhaitez définir une nouvelle API dans laquelle les valeurs non définies ont un sens , utilisez Option Pattern (peut être familier des langages fonctionnels). Il présente les avantages suivants:

  • L'API indique explicitement si une entrée ou une sortie existe ou non.
  • Le compilateur vous oblige à gérer & "; undefined &"; cas.
  • L'option est une monade , il n’est donc pas nécessaire de vérifier les valeurs de base nullement verbose, utilisez simplement map / foreach / getOrElse ou un combinateur similaire pour utiliser en toute sécurité la valeur (exemple) .

Java 8 intègre un Optional classe (recommandé); pour les versions antérieures, il existe des alternatives de bibliothèque, par exemple Guava 's < a href = "https://google.github.io/guava/releases/19.0/api/docs/com/google/common/base/Optional.html" rel = "noreferrer"> Option ou FonctionnelJava ?: . Mais comme beaucoup de modèles de style fonctionnel, utiliser Option en Java (même 8) donne un certain nombre de standards, que vous pouvez réduire avec un langage JVM moins détaillé, par exemple. Scala ou Xtend.

Si vous devez utiliser une API susceptible de renvoyer des valeurs NULL , vous ne pouvez pas faire grand-chose en Java. Xtend et Groovy ont l'opérateur Elvis ?. et le opérateur de déréférence null-safe <=>, mais notez que cette fonction renvoie la valeur null dans le cas d'une référence null, donc il " diffère " le traitement approprié de null.

Seulement pour cette situation -

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

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

entraînera un NullPointerException si foo n'existe pas.

Vous pouvez éviter cela si vous comparez votre String s comme ceci:

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

Avec Java 8, la nouvelle java.util.Optional classe permet de résoudre 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, clarifie le contrat de l'API pour le développeur client.

Ils travaillent comme ça:

Un objet facultatif pour un type donné (Fruit) est créé en tant que type de retour d'une méthode. Il peut être vide ou contenir un fruits 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 recherchons une liste de map() (orElse()) pour une instance Fruit donnée:

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

Vous pouvez utiliser l'opérateur Optional pour effectuer un calcul - ou en extraire une valeur - d'un objet facultatif. null vous permet de remplacer les valeurs manquantes.

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

Bien sûr, le contrôle de la valeur null / empty est toujours nécessaire, mais au moins le développeur est conscient du fait que la valeur peut être vide et que le risque d'oubli du contrôle est limité.

Dans une API créée à partir de rien, à l'aide de orElse chaque fois qu'une valeur de retour peut être vide, et renvoyant un objet brut uniquement lorsqu'elle ne peut pas être ifPresent (convention), le code client peut abandonner les vérifications nulles sur les valeurs de retour d'objet simple ...

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

<=> propose d'autres méthodes pratiques, telles que <=> qui permettent d'utiliser une valeur par défaut et <=> qui fonctionnent avec expressions lambda .

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

En fonction du type d'objets que vous vérifiez, vous pourrez peut-être utiliser certaines des classes de Apache commons telles que: apache commons lang et collections apache commons

Exemple:

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

ou (selon ce que vous devez vérifier):

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

La classe StringUtils n'est qu'un parmi beaucoup d'autres. il y a pas mal de bonnes classes dans les communes qui font de la manipulation sans danger.

Voici un exemple d'utilisation de la validation nulle en 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 possède également les mêmes fonctionnalités dans son package, voir la bibliothèque (spring-2.4.6.jar)

Exemple d'utilisation de ce classf statique à partir de spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
  • Si vous considérez qu'un objet ne doit pas être nul (ou s'il s'agit d'un bogue), utilisez une assertion.
  • Si votre méthode n'accepte pas les paramètres null, dites-le dans le javadoc et utilisez une assertion.

Vous devez rechercher object! = null uniquement si vous souhaitez gérer le cas où l'objet peut être null ...

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

Je suis fan de & "Fail fast" & "; code. Demandez-vous - faites-vous quelque chose d'utile dans le cas où le paramètre est nul? Si vous n'avez pas de réponse claire à ce que votre code devrait faire dans ce cas ... I.e. elle ne devrait jamais être nulle en premier lieu, alors ignorez-la et laissez une exception NullPointerException être levée. Le code d'appel donnera autant de sens à un NPE qu'à un IllegalArgumentException, mais il sera plus facile pour le développeur de déboguer et de comprendre ce qui s'est mal passé si un NPE est lancé plutôt que votre code essayant d'exécuter une autre imprévu. logique - ce qui a finalement pour résultat que l'application échoue de toute façon.

Le cadre de gestion des collections de Google offre un moyen simple et élégant de réaliser le contrôle nul.

Il existe une méthode dans une classe de bibliothèque comme celle-ci:

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

Et l'utilisation est (avec import static):

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

Ou dans votre exemple:

checkNotNull(someobject).doCalc();

Vous avez parfois des méthodes qui agissent sur ses paramètres et qui définissent une opération symétrique:

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

Si vous savez que b ne peut jamais être nul, vous pouvez simplement l'échanger. C'est le plus utile pour les égaux: Au lieu de foo.equals("bar"); mieux faire "bar".equals(foo);.

Plutôt que le motif d'objet Null (qui a ses utilisations), vous pouvez envisager des situations où l'objet null est un bogue.

Lorsque l'exception est levée, examinez la trace de la pile et corrigez le bogue.

Java 7 a une nouvelle classe d’utilitaires java.util.Objects sur laquelle il existe une méthode requireNonNull(). Cela ne fait que lancer un NullPointerException si son argument est null, mais cela nettoie un peu le code. Exemple:

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

Cette méthode est particulièrement utile pour vérifier juste avant une affectation. dans un constructeur, où chaque utilisation peut économiser trois lignes de code:

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

devient

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}

Null n'est pas un "problème". Il fait partie intégrante d’un ensemble d’outils de modélisation complet . Le logiciel vise à modéliser la complexité du monde et à supporter son fardeau. Null indique "Pas de données" ou "Inconnu" en Java, etc. Il est donc approprié d'utiliser des valeurs NULL à ces fins. Je ne préfère pas le motif 'Null object'; Je pense que ça monte le ' qui gardera les gardiens 'problème.
Si vous me demandez quel est le nom de ma petite amie, je vous dirai que je n'ai pas de petite amie. Dans le langage Java, je retournerai null. Une alternative serait de lancer une exception significative pour indiquer un problème qui ne peut pas (ou ne veut pas être) être résolu ici et le déléguer quelque part plus haut dans la pile pour réessayer ou signaler une erreur d'accès aux données à l'utilisateur.

  1. Pour une "question inconnue", donnez une "réponse inconnue". (Soyez null-safe si cela est correct du point de vue commercial) Vérification des arguments pour null une fois dans une méthode avant utilisation évite à plusieurs appelants de les consulter avant un appel.

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

    Précédent mène au flux logique normal pour obtenir aucune photo d'une petite amie inexistante de ma photothèque.

    getPhotoOfThePerson(me.getGirlfriend())
    

    Et cela correspond à la nouvelle API Java à venir (regard vers l'avenir)

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

    Bien que ce soit plutôt un "processus normal" que de ne pas trouver une photo stockée dans la base de données pour une personne, j’avais l'habitude d'utiliser des paires comme ci-dessous pour d'autres cas

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

    Et n'aimez pas taper <alt> + <shift> + <j> (générer javadoc dans Eclipse) et écrire trois mots supplémentaires pour votre API publique. Ce sera plus que suffisant pour tous sauf ceux qui ne lisent pas la documentation.

    /**
     * @return photo or null
     */
    

    ou

    /**
     * @return photo, never null
     */
    
  2. Ceci est un cas plutôt théorique et dans la plupart des cas, vous devriez préférer une API java null safe (au cas où elle sera publiée dans 10 ans), mais NullPointerException est une sous-classe d'un Exception. Il s’agit donc d’une forme Throwable qui indique les conditions qu’une application raisonnable peut vouloir intercepter ( javadoc )! Pour utiliser le premier avantage des exceptions et séparer le code de gestion des erreurs du code "normal" ( selon les créateurs de Java ), il convient, comme pour moi, de saisir getPhotoDataSource().

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

    Des questions pourraient se poser:

    Q. Que faire si getPhotoByName() renvoie la valeur null?
    A. Cela dépend de la logique métier. Si je ne parviens pas à trouver un album photo, je ne vous montrerai aucune photo. Et si appContext n'est pas initialisé? La logique métier de cette méthode résout ce problème. Si la même logique doit être plus stricte, une exception est générée dans la logique métier et une vérification explicite de null doit être utilisée (cas 3). La nouvelle API Java Null-safe convient mieux ici pour spécifier de manière sélective ce qui implique et ce qui n’implique pas d’être initialisé pour être rapide après défaillance en cas d’erreur de programmation.

    Q. Du code redondant pourrait être exécuté et des ressources inutiles pourraient être récupérées.
    R. Cela pourrait se produire si PreparedStatement essayait d'ouvrir une connexion de base de données, créez ThreadDeath et utilisait enfin le nom de la personne comme paramètre SQL. L’approche pour une question inconnue donne une réponse inconnue (cas n ° 1) fonctionne ici. Avant de récupérer des ressources, la méthode doit vérifier les paramètres et renvoyer un résultat "inconnu" si nécessaire.

    Q. Cette approche a un impact négatif sur les performances en raison de l'ouverture de la fermeture de l'essai.
    A. Le logiciel doit être facile à comprendre et à modifier en premier lieu. Ce n’est qu’après cela que l’on pourrait penser à la performance, et seulement au besoin! et en cas de besoin! ( source ), et beaucoup d'autres).

    PS Cette approche sera aussi raisonnable à utiliser que le code de traitement des erreurs distinct de & "Normal &"; Le principe de code est raisonnable à utiliser dans certains endroits. Prenons l'exemple suivant:

    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. Pour ceux qui ont le moins de voix possible (et pas si vite pour lire la documentation), je voudrais dire que je n’ai jamais attrapé une exception de type pointeur nul (NPE) dans ma vie. Mais cette possibilité a été conçue intentionnellement par les créateurs de Java, car NPE est une sous-classe de Error. Nous avons un précédent dans l’historique Java lorsque <=> est un <=> pas parce qu’il s’agit d’une erreur d’application, mais uniquement parce que cela n’a pas été prévu! Combien de NPE convient pour être un <=> que <=>! Mais ce n’est pas le cas.

  3. Recherchez l'option "Pas de données" uniquement si la logique métier l'implique.

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

    et

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

    Si appContext ou dataSource n'est pas initialisé non géré, NullPointerException va tuer le thread en cours et sera traité par Thread.defaultUncaughtExceptionHandler (pour que vous définissiez et utilisiez votre enregistreur favori ou un autre mécanisme de notification). Si non défini, ThreadGroup # uncaughtException imprimera stacktrace en erreur système. Il faut surveiller le journal des erreurs d’application et ouvrir le problème Jira pour chaque exception non gérée qui est en fait une erreur d’application. Le programmeur devrait corriger un bogue quelque part dans l'initialisation.

En définitive, le seul moyen de résoudre complètement ce problème consiste à utiliser un autre langage de programmation:

  • En Objective-C, vous pouvez faire l'équivalent d'appeler une méthode sur nil et rien ne se passera absolument. Cela rend la plupart des vérifications nulles inutiles, mais peut rendre les erreurs beaucoup plus difficiles à diagnostiquer.
  • Dans Nice , un langage dérivé de Java, il existe deux versions de tous les types : une version potentiellement nulle et une version non nulle. Vous ne pouvez invoquer des méthodes que sur des types non nuls. Les types potentiellement nuls peuvent être convertis en types non-nuls grâce à une vérification explicite de null. Il est ainsi beaucoup plus facile de savoir où les contrôles nulles sont nécessaires et où ils ne le sont pas.

Problème & commun " en Java en effet.

Tout d'abord, mes réflexions à ce sujet:

Je considère qu'il est mauvais de & "manger &"; quelque chose quand NULL a été passé où NULL n'est pas une valeur valide. Si vous ne quittez pas la méthode avec une sorte d'erreur, cela signifie que rien ne s'est mal passé dans votre méthode, ce qui n'est pas vrai. Ensuite, vous renvoyez probablement null dans ce cas, et dans la méthode de réception, vous vérifiez à nouveau que la valeur est null, et elle ne finit jamais, et vous vous retrouvez avec & Quot; if! = Null & Quot ;, etc ..

Donc, IMHO, null doit être une erreur critique empêchant toute exécution ultérieure (c'est-à-dire, où null n'est pas une valeur valide).

Voici comment je résous ce problème:

Tout d'abord, je suis cette convention:

  1. Toutes les méthodes publiques / API vérifient toujours ses arguments pour null
  2. Toutes les méthodes privées ne recherchent pas la valeur null car ce sont des méthodes contrôlées (laissez mourir avec l'exception nullpointer au cas où cela n'aurait pas été géré ci-dessus)
  3. Les seules autres méthodes qui ne vérifient pas la valeur null sont les méthodes utilitaires. Ils sont publics, mais si vous les appelez pour une raison quelconque, vous savez quels paramètres vous transmettez. C'est comme essayer de faire bouillir de l'eau dans la bouilloire sans fournir d'eau ...

Enfin, dans le code, la première ligne de la méthode publique est la suivante:

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

Notez que addParam () renvoie self, ce qui vous permet d'ajouter plus de paramètres à vérifier.

La méthode validate() jettera vérifié ValidationException si l'un des paramètres est null (coché ou décoché, il s'agit plutôt d'un problème de conception / goût, mais mon <=> est coché).

void validate() throws ValidationException;

Le message contiendra le texte suivant si, par exemple, " plans " est nul:

" La valeur de l'argument non valide est nulle pour le paramètre [plans] "

Comme vous pouvez le constater, la deuxième valeur de la méthode addParam () (chaîne) est nécessaire pour le message de l'utilisateur, car vous ne pouvez pas facilement détecter le nom de la variable transmise, même avec réflexion (de toute façon pas le sujet de cette publication). .).

Et oui, nous savons qu'au-delà de cette ligne, nous ne rencontrerons plus de valeur nulle. Nous invoquerons donc en toute sécurité des méthodes sur ces objets.

De cette façon, le code est propre, facile à gérer et lisible.

Poser cette question indique que vous pouvez être intéressé par les stratégies de traitement des erreurs. L'architecte de votre équipe doit décider de la manière de traiter les erreurs. Il existe plusieurs façons de procéder:

  1. autorisez les exceptions à se propager - capturez-les à la "boucle principale" ou dans une autre routine de gestion.

    • recherchez les conditions d'erreur et les gérez correctement

Bien sûr, jetez également un coup d'oeil à la programmation orientée aspect - elle dispose également de moyens astucieux pour insérer if( o == null ) handleNull() dans votre code secondaire.

Outre assert, vous pouvez utiliser les éléments suivants:

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

C'est légèrement mieux que:

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



    .....
}

N'utilisez jamais la valeur null. Ne le permettez pas.

Dans mes classes, la plupart des champs et des variables locales ont des valeurs par défaut non nulles et j'ajoute des instructions de contrat (des assertions permanentes) partout dans le code pour garantir le respect de cette règle (car elle est plus succincte et plus expressive). que de le laisser apparaître comme un NPE et de devoir ensuite résoudre le numéro de ligne, etc.).

Une fois que j'ai adopté cette pratique, j'ai remarqué que les problèmes semblaient se résoudre d'eux-mêmes. Vous détecteriez des problèmes beaucoup plus tôt dans le processus de développement par accident et vous vous rendriez compte que vous aviez un point faible… et plus important encore… cela aide à résumer les préoccupations de différents modules, différents modules pouvant se faire «confiance» les uns les autres, code avec if = null else constructions!

Il s’agit d’une programmation défensive qui aboutit à un code beaucoup plus propre à long terme. Toujours désinfecter les données, par exemple ici en appliquant des normes rigides, et les problèmes disparaissent.

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

Les contrats ressemblent à des tests en mini-unités qui sont toujours exécutés, même en production, et lorsque les choses échouent, vous savez pourquoi, plutôt que de choisir un NPE aléatoire, vous devez en quelque sorte le découvrir.

Guava, une bibliothèque de base très utile de Google, possède une API intéressante et utile pour éviter les valeurs NULL. UtiliserAndAvoidingNullExplained est très utile.

Comme expliqué dans le wiki:

  

Optional<T> est un moyen de remplacer une référence T nullable par un   valeur non nulle. Un optionnel peut soit contenir une référence T non nulle   (auquel cas nous disons que la référence est & "; présente &";), ou elle peut contenir   rien (auquel cas nous disons que la référence est & "absent &";). Ce n'est jamais   dit à " contenir null. "

Utilisation:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

Il s’agit d’un problème très courant pour tous les développeurs Java. Il existe donc un support officiel en Java & Nbsp; 8 pour résoudre ces problèmes sans code encombré.

Java 8 a introduit java.util.Optional<T>. C'est un conteneur qui peut contenir ou non une valeur non nulle. Java 8 a fourni un moyen plus sûr de gérer un objet dont la valeur peut être nulle dans certains cas. Il s'inspire des idées de Haskell et de Scala .

En un mot, la classe Optional inclut des méthodes pour traiter explicitement les cas où une valeur est présente ou absente. Cependant, l’avantage par rapport aux références nulles est que les options & Lt; T & Gt; classe vous oblige à penser au cas où la valeur n’est pas présente. Par conséquent, vous pouvez empêcher les exceptions de pointeur null involontaires.

Dans l'exemple ci-dessus, nous avons une usine de service à domicile qui renvoie un descripteur à plusieurs appliances disponibles à la maison. Mais ces services peuvent ou non être disponibles / fonctionnels; cela signifie que cela peut entraîner une exception NullPointerException. Au lieu d’ajouter une condition null if avant d’utiliser un service, encapsulons-le dans Facultatif & Lt; Service & Gt;.

EMBALLAGE À L'OPTION < T >

Considérons une méthode pour obtenir la référence d'un service auprès d'une usine. Au lieu de renvoyer la référence de service, encapsulez-la avec facultatif. Cela permet à l'utilisateur de l'API de savoir que le service retourné peut ou non être disponible / fonctionnel, utiliser de manière défensive

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

Comme vous le voyez, Optional.ofNullable() fournit un moyen simple d’obtenir la référence encapsulée. Il existe un autre moyen d’obtenir la référence de Optional, soit Optional.empty() & Amp; Optional.of() Une pour retourner un objet vide au lieu de réaccorder null et l'autre pour envelopper un objet non nullable, respectivement.

ALORS QUELLE FAÇON EXACTEMENT D’EVITER UNE VÉRIFICATION NULL?

Une fois que vous avez encapsulé un objet de référence, Optional fournit de nombreuses méthodes utiles pour appeler des méthodes sur une référence encapsulée sans NPE.

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

Optional.ifPresent appelle le consommateur donné avec une référence s'il s'agit d'une valeur non nulle. Sinon, cela ne fait rien.

@FunctionalInterface
public interface Consumer<T>

Représente une opération qui accepte un seul argument d'entrée et ne renvoie aucun résultat. Contrairement à la plupart des autres interfaces fonctionnelles, Consumer devrait fonctionner via des effets secondaires. C'est tellement propre et facile à comprendre. Dans l'exemple de code ci-dessus, HomeService.switchOn(Service) est appelé si la référence de détention facultative est non nulle.

Nous utilisons très souvent l'opérateur ternaire pour vérifier la condition nulle et renvoyer une valeur alternative ou une valeur par défaut. Facultatif fournit un autre moyen de gérer la même condition sans vérifier null. Optional.orElse (defaultObj) renvoie defaultObj si le type facultatif a une valeur null. Utilisons ceci dans notre exemple de code:

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

Maintenant, HomeServices.get () fait la même chose, mais d’une meilleure façon. Il vérifie si le service est déjà initialisé ou non. Si c'est le cas, retournez-le ou créez un nouveau service. Facultatif & Lt; T & Gt; .orElse (T) aide à renvoyer une valeur par défaut.

Enfin, voici notre NPE ainsi que le code sans vérification:

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){
         //...
    }
}

Le message complet est NPE ainsi que le code sans vérification sans code & # 8230; Vraiment? .

J'aime les articles de Nat Pryce. Voici les liens:

Dans les articles, il existe également un lien vers un référentiel Git pour un type Java Maybe que je trouve intéressant, mais je ne pense pas que cela seul puisse diminuer la vérifier le code ballonnement. Après des recherches sur Internet, je pense que le ! = Null gonflement du code pourrait être réduit principalement par une conception soignée.

J'ai essayé le NullObjectPattern mais pour moi ce n'est pas toujours la meilleure façon de faire. Il y a parfois quand un & "Aucune action &"; n'est pas approprié.

NullPointerException est une exception d'exécution qui signifie que c'est une faute des développeurs et qui a suffisamment d'expérience pour vous dire exactement où se trouve l'erreur.

Passons maintenant à la réponse:

Essayez de rendre tous vos attributs et leurs accesseurs aussi privés que possible ou évitez de les exposer aux clients. Vous pouvez bien sûr avoir les valeurs d'argument dans le constructeur, mais en réduisant l'étendue, vous ne laissez pas la classe client transmettre une valeur non valide. Si vous devez modifier les valeurs, vous pouvez toujours créer un nouveau object. Vous vérifiez les valeurs dans le constructeur uniquement une fois et dans le reste des méthodes, vous pouvez être presque sûr que les valeurs ne sont pas nulles.

Bien entendu, l’expérience est le meilleur moyen de comprendre et d’appliquer cette suggestion.

Byte!

La meilleure alternative à Java 8 ou aux versions plus récentes consiste probablement à utiliser Optional classe.

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

Ceci est particulièrement utile pour les longues chaînes de valeurs null possibles. Exemple:

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

Exemple sur la manière de lever une exception sur null:

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

Java 7 a introduit le Objects.requireNonNull méthode qui peut être utile lorsque quelque chose doit être vérifié pour la non-nullité. Exemple:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

Puis-je répondre plus généralement!

Nous généralement sommes confrontés à ce problème lorsque les méthodes obtiennent les paramètres inattendus (un appel de méthode incorrect est la faute du programmeur). Par exemple: vous vous attendez à obtenir un objet, vous obtenez plutôt un null. Vous vous attendez à obtenir une chaîne avec au moins un caractère, mais plutôt une chaîne vide ...

Donc, il n'y a pas de différence entre:

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

}

ou

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

Ils veulent tous les deux s'assurer que nous recevons des paramètres valides avant de faire toute autre fonction.

Comme mentionné dans d'autres réponses, pour éviter les problèmes ci-dessus, vous pouvez suivre le modèle Conception par contrat . Veuillez consulter http://en.wikipedia.org/wiki/Design_by_contract .

Pour implémenter ce modèle en Java, vous pouvez utiliser des annotations Java essentielles telles que javax.annotation.NotNull ou utiliser des bibliothèques plus sophistiquées telles que Hibernate Validator .

Juste un échantillon:

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

Maintenant, vous pouvez développer en toute sécurité la fonction principale de votre méthode sans avoir besoin de vérifier les paramètres d'entrée, ils protègent vos méthodes des paramètres inattendus.

Vous pouvez aller plus loin et vous assurer que vous ne pouvez créer que des pojos valides dans votre application. (échantillon du site du validateur hibernate)

public class Car {

   @NotNull
   private String manufacturer;

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

   @Min(2)
   private int seatCount;

   // ...
}

J'ignore fortement les réponses qui suggèrent d'utiliser les objets nuls dans toutes les situations. Cela risque de rompre le contrat et d’enterrer de plus en plus profond les problèmes au lieu de les résoudre, sans mentionner le fait qu’une utilisation inappropriée créera une autre pile de code passe-partout qui nécessitera un entretien ultérieur.

En réalité, si quelque chose retourné par une méthode peut être null et que le code appelant doit prendre une décision à ce sujet, il devrait y avoir un appel précédent qui assure l’état.

N'oubliez pas non plus que ce modèle d'objet nul aura besoin de beaucoup de mémoire s'il est utilisé sans précaution. Pour cela, l’instance de NullObject doit être partagée entre les propriétaires et ne pas être une instance d’unigue pour chacun d’entre eux.

De plus, je ne recommanderais pas l'utilisation de ce modèle où le type est censé être une représentation de type primitif - comme les entités mathématiques, qui ne sont pas des scalaires: vecteurs, matrices, nombres complexes et objets POD (Plain Old Data), qui sont destinés à conserver l'état sous la forme de types intégrés Java. Dans ce dernier cas, vous appelez des méthodes getter avec des résultats arbitraires. Par exemple, que doit renvoyer une méthode NullPerson.getName ()?

Cela vaut la peine d’envisager de tels cas afin d’éviter des résultats absurdes.

  1. N'initialisez jamais les variables à null.
  2. Si (1) n'est pas possible, initialisez toutes les collections et les tableaux pour vider les collections / tableaux.

Faites cela dans votre propre code et vous pourrez éviter les contrôles! = null.

La plupart du temps, les vérifications nulles semblent protéger les boucles sur les collections ou les tableaux, donc initialisez-les vides, vous n'aurez pas besoin de vérifications nulles.

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

Il y a une petite surcharge en cela, mais cela en vaut la peine pour un code plus propre et moins NullPointerExceptions.

C’est l’erreur la plus courante survenue pour la plupart des développeurs.

Nous avons plusieurs façons de gérer cela.

Approche 1:

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

notNull (objet Object, message de chaîne)

Approche 2:

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

Approche 3:

@isNull @Nullable  // using annotation based validation

Approche 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;
}
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top