web-dev-qa-db-fra.com

Attraper une exception de pointeur nulle est-il une odeur de code?

Récemment, un de mes collègues a écrit dans un code pour intercepter une exception de pointeur null autour d'une méthode entière et renvoyer un seul résultat. J'ai souligné comment le pointeur null pouvait avoir plusieurs raisons, nous l'avons donc remplacé par un contrôle défensif.

Cependant, attraper NullPointerException ne me semblait pas juste. Dans mon esprit, les exceptions de pointeur Null sont le résultat d'un code incorrect et ne constituent pas une exception attendue dans le système.

Existe-t-il des cas où il est logique d'attraper une exception de pointeur nul? 

57
Drew

Oui, attraper une RuntimeException est presque toujours une odeur de code. Le C2 Wiki semble être d’accord.

Une exception serait probablement des morceaux de code spécialement défensifs qui utilisent un code assez aléatoire à partir d'autres modules. Des exemples de telles structures défensives sont le EDT , ThreadPools/Executors et le système de plug-in.

35
Joachim Sauer

Je peux penser à exactement un usage pour toujours attraper un NullPointerException:

catch (NullPointerException) {
    ApplyPainfulElectricShockToProgrammer();
}
22

J'ai dû parfois intercepter l'exception nullpointer à cause d'un bogue dans la bibliothèque de la troisième partie. La bibliothèque que nous avons utilisée a lancé cette exception et nous ne pouvions rien y faire.

Dans ce cas, c'estOKpour l'attraper, sinon pas.

15
Shervin Asgari

Ça dépend.

Quelle est l'expérience de ce collègue? Est-ce qu'il fait cela par ignorance/paresse ou y a-t-il une vraie bonne raison à cela? (comme c'est le fil conducteur au dessus de tout le reste et ne devrait jamais mourir?) 

90% des fois que capturer une exception d'exécution est fausse, 99%, capturer une exception NullPointerException est erronée (si la raison est "J'en ai eu beaucoup ..." alors tout le programmeur a tort et vous devriez regarder prendre soin du reste du code qu'il fait) 

Mais dans certaines circonstances, attraper une exception NullPointerException peut être acceptable. 

7
OscarRyz

Marrant

Je viens de trouver quelque chose qui ne devrait pas être fait au travail:

public static boolean isValidDate(final String stringDateValue) {
    String exp = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$";
    boolean isValid = false;
    try {
        if (Pattern.matches(exp, stringDateValue)) {
            String[] dateArray = stringDateValue.split("/");
            if (dateArray.length == 3) {
                GregorianCalendar gregorianCalendar = new GregorianCalendar();
                int annee = new Integer(dateArray[2]).intValue();
                int mois = new Integer(dateArray[1]).intValue();
                int jour = new Integer(dateArray[0]).intValue();

                gregorianCalendar = new GregorianCalendar(annee, mois - 1,
                        jour);
                gregorianCalendar.setLenient(false);
                gregorianCalendar.get(GregorianCalendar.YEAR);
                gregorianCalendar.get(GregorianCalendar.MONTH);
                gregorianCalendar.get(GregorianCalendar.DAY_OF_MONTH);
                isValid = true;
            }
        }
    } catch (Exception e) {
        isValid = false;
    }
    return isValid;
}

baaad :)

Le développeur souhaitait que le calendrier soulève des exceptions de ce type:

Java.lang.IllegalArgumentException: DAY_OF_MONTH
    at Java.util.GregorianCalendar.computeTime(GregorianCalendar.Java:2316)
    at Java.util.Calendar.updateTime(Calendar.Java:2260)
    at Java.util.Calendar.complete(Calendar.Java:1305)
    at Java.util.Calendar.get(Calendar.Java:1088)

invalider les valeurs ...

Oui ça marche mais ce n'est pas une très bonne pratique ...

La levée des exceptions (en particulier le remplissage de la trace de la pile) coûte beaucoup plus que la vérification manuelle des données sans exception ...

3
Sebastien Lorber

En général, je pense que c'est une odeur de code; il me semble que les contrôles défensifs sont meilleurs. J'étendrais cela à la plupart des exceptions non vérifiées, sauf dans les boucles d'événements, etc. qui veulent intercepter toutes les erreurs pour la génération de rapports/journalisation.

L'exception à laquelle je peux penser concerne un appel à une bibliothèque qui ne peut pas être modifié et qui peut générer une exception de pointeur nul en réponse à un échec d'assertion difficile à vérifier de manière proactive.

3
Michael Ekstrand

C'est mauvais, mais cela pourrait produire du bytecode optimisé.

Si Integer i n'est pas null la plupart du temps, le contrôle diminue les performances globales. Le chèque lui-même coûte 3 instructions (0-4). Le cas entier prend alors 7 instructions (0-14).

public class IfNotNull {

    public Integer i;

    public String getIAsString() {
        if (i != null) {
            return i.toString();
        } else {
            return "";
        }
    }
}

  public Java.lang.String getIAsString();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field i:Ljava/lang/Integer;
       4: ifnull        15
       7: aload_0       
       8: getfield      #2                  // Field i:Ljava/lang/Integer;
      11: invokevirtual #3                  // Method Java/lang/Integer.toString:()Ljava/lang/String;
      14: areturn       // <- here we go
      15: ldc           #4                  // String 
      17: areturn 

Suivant l’approche EAFP qui est courante dans le monde Python. Le cas null sera coûteux, mais nous n’aurons besoin que de 4 instructions (0-7) pour le cas not null

public class TryCatch {

    public Integer i;

    public String getIAsString() {
        try {
            return i.toString();
        } catch (NullPointerException npe) {
            return "";
        }
    }
}

  public Java.lang.String getIAsString();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field i:Ljava/lang/Integer;
       4: invokevirtual #3                  // Method Java/lang/Integer.toString:()Ljava/lang/String;
       7: areturn       // <- here we go
       8: astore_1      
       9: ldc           #5                  // String a
      11: areturn       
    Exception table:
       from    to  target type
           0     7     8   Class Java/lang/NullPointerException

Qui sait, si le compilateur JIT peut optimiser cela? 

3
PJA

Le seul endroit où vous devriez attraper une exception NullPointerException (ou plus précisément, n'importe quel Throwable) est à une limite de niveau supérieur ou système afin que votre programme ne tombe pas en panne et puisse être récupéré. Par exemple, la configuration d’une page d’erreur dans votre fichier web.xml fournit un ensemble complet permettant à une application Web de récupérer d’une exception et d’en avertir l’utilisateur.

2
GreenieMeanie

C'est certainement.

La plupart du temps, vos variables ne devraient pas être nul pour commencer. De nombreux nouveaux langages proposent une prise en charge intégrée des types de référence non nullables, c'est-à-dire des types dont la garantie de ne jamais être nulle.

Pour les moments où votre valeur entrante est autorisée à être nulle, vous devez effectuer une vérification. Mais les exceptions sont définitivement un mauvais moyen de le faire. 

Une instruction if prend peut-être trois instructions à exécuter et constitue une vérification locale (ce qui signifie que vous effectuez la vérification au même endroit que vous avez besoin de la garantie). 

En revanche, l’utilisation d’une exception peut prendre beaucoup plus d’instructions: le système tente de rechercher la méthode, échoue, recherche dans la table des exceptions le gestionnaire d’exceptions approprié, saute à cet emplacement, exécute le gestionnaire et saute à nouveau. De plus, la vérification est potentiellement non locale. Si votre code ressemble à ceci: 

try
  return contacts.find("Mom").getEmail()
catch (NullPointerException e)
  return null

Vous ne savez pas si le NPE a été lancé dans 'getEmail' ou dans 'find'. 

Une solution technique pire à un modèle très, très commun écrit de manière plus obscure? Ce n'est pas un rang, mais ça sent vraiment mauvais: /

2
Tac-Tics

La capture d'une exception de pointeur NULL dépend vraiment du contexte ... il faut s'efforcer d'éviter les règles absolues strictes ... les règles doivent être appliquées dans le contexte - vous voulez intercepter cette exception et placer le logiciel entier dans un état STABLE - ne rien faire ou presque presque rien. Toutes ces règles de codage doivent être bien comprises 

À ce stade, examinez votre logiciel AUDIT TRACE ... que vous devriez utiliser et découvrez la SOURCE de cette exception.

L'idée qu'une exception de pointeur NULL ne doit jamais se produire doit être vérifiable. Commencez par effectuer une analyse statique ... (ce qui est plus difficile si du code/des composants tiers entrent en jeu), puis effectuez une recherche exhaustive de l'espace d'état à l'aide d'outils pertinents.

x

1
Xofo

Et ça:

try
{
    foo.x = bar;
}
catch (NullPointerException e)
{
    // do whatever needs to be done
}

comme une micro-optimisation quand foo peut être nul, mais presque jamais?

L'idée est la suivante:

  • Une vérification NULL explicite prend une seule instruction machine

  • D'autre part, la vérification NULL dans la deuxième version peut être effectuée en laissant l'accès NULL se produire, en capturant le SIGSEGV et en lançant une exception NullPointerException. Ceci est gratuit si l'objet n'est pas NULL.

1
Demi

Il y a longtemps que j'ai eu une utilisation. Une bibliothèque particulièrement stupide lancerait NullPointerException à la demande d'un objet dans une collection par clé et si l'objet était introuvable. Il n'y avait pas d'autre moyen de chercher que par clé et aucun moyen de vérifier si l'objet existait.

Quelque temps plus tard, nous avons démarré le fournisseur et commencé à modifier la bibliothèque. Maintenant, la bibliothèque lève une meilleure exception (mon changement) et a une fonction de vérification (le changement de quelqu'un d'autre).

Bien sûr, je finissais toujours avec exactement une ligne à l'intérieur du bloc try. Plus et je serais moi-même coupable de mauvais code.

1
Joshua

Il peut être nécessaire d'attraper les NPE (tous les RTE en fait) pour terminer proprement une application basée sur une interface graphique Swing. 

edit: dans ce cas, cela se fait habituellement via UncaughtExceptionHandler.

1
Rhangaun

Oui, en Java, il est nécessaire de rechercher une exception NullPointerException.

Lancé lorsqu'une application tente d'utiliser null dans un cas où un objet est requis. Ceux-ci inclus: 

Appel de la méthode d'instance d'un objet null. Accéder ou modifier le champ d’un objet null. Prendre la longueur de null comme s'il s'agissait d'un tableau. Accéder ou modifier les slots de null comme s'il s'agissait d'un tableau. Lancer null comme s'il s'agissait d'une valeur Throwable. 

Les applications doivent utiliser des instances de cette classe pour indiquer d'autres utilisations illégales de l'objet null. 

NullPointerException dans d’autres langues lors de la lecture de fichiers texte (XML), dont les enregistrements n’ont pas été validés avec le caractère ASCII et le format d’enregistrement appropriés.

0
mpurinton

Si le programmeur est débutant, il peut avoir l’habitude d’attraper toutes les exceptions qui l’empêchent d’obtenir le résultat final. Cela ne devrait pas être diverti par les réviseurs de code.

Attraper une exception RuntimeException est mauvais. Mais si cela est vraiment nécessaire, un commentaire dans le code sera vraiment utile pour les futurs programmeurs qui travailleront sur ce code. Si vous ne pouvez pas écrire un commentaire raisonnable pour les attraper, vous devez les éviter. période.

0
dnsh

Cela dépend vraiment de la définition de l'interface. La gestion des NPE non structurés est aussi grave que d’attraper Exception ou Throwable.

Les valeurs null sont utiles pour identifier un état non initialisé plutôt que d'utiliser une chaîne vide ou max_int ou autre. Une fois que j'utilise régulièrement null, c'est à des endroits où un objet de rappel n'est pas pertinent.

J'aime beaucoup l'annotation @Nullable fournie par Guice.

http://code.google.com/docreader/#p=google-guice&s=google-guice&t=UseNullable

Pour éliminer NullPointerExceptions dans votre base de code, vous devez être discipliné sur les références nulles. Nous avons été réussi à cela en suivant et appliquer une règle simple:

Chaque paramètre est non nul sauf si explicitement spécifié. Le Google La bibliothèque de collections et JSR-305 ont API simples pour obtenir une valeur null sous contrôle. Preconditions.checkNotNull peut être utilisé pour un échec rapide si un null la référence est trouvée et @Nullable peut être utilisé pour annoter un paramètre qui permet la valeur nulle.

Guice interdit null par défaut. Ce sera refuser d'injecter null, en échouant avec un ProvisionException à la place. Si null est permis par votre classe, vous pouvez annotez le champ ou le paramètre avec @ Nullable. Guice reconnaît tout @Nullable annotation, comme edu.umd.cs.findbugs.annotations.Nullable ou javax.annotation.Nullable.

0
Stevko

Attraper une exception NullPointerException peut être utile si votre méthode appelle une interface externe (ou une API SOAP) et qu'il est possible que la valeur renvoyée soit Null. Autre que cela, il n'y a pas un avantage énorme à attraper ces exceptions.

0
AV.

J'essaie de garantir des résultats à partir de mes interfaces, mais si une bibliothèque ou un code donné par quelqu'un peut produire une valeur nulle, il est viable. Bien sûr, ce que vous faites une fois que vous l'attrapez est à vous. Parfois, cela n'a aucun sens de vérifier la valeur null, et si vous l'attrapez, vous avez une autre méthode pour résoudre le problème qui peut ne pas être aussi bonne mais qui fait le travail.

Ce que je dis, c'est d'utiliser des exceptions pour ce que vous pouvez, c'est une très bonne fonctionnalité linguistique.

0
Tore