2
votes

À quelle fréquence et où effectuer des vérifications nulles

J'essaie d'éviter d'exagérer les vérifications nulles, mais en même temps je veux faire une vérification nulle chaque fois qu'il a besoin de rendre le code robuste. Mais parfois, j'ai l'impression que cela commence à être tellement défensif car je n'implémente pas d'API. Ensuite, j'évite certaines vérifications nulles, mais lorsque je lance des tests unitaires, cela commence à toujours attendre les exceptions d'exécution. Quelle est la bonne approche, comment ressentir l'équilibre. Voici les notes que j'ai collectées jusqu'à présent:

  • évitez les vérifications nulles inutiles dans les méthodes privées de la classe d'instance, car les vérifications nulles appropriées sont effectuées avant et la classe d'instance en est responsable, pas chaque fonction
  • effectuer des vérifications nulles dans les méthodes publiques et expliquer le comportement de la fonction dans le contrat pour l'usage public
  • faire des vérifications nulles dans le constructeur

Permettez-moi de donner un exemple factice où ma confusion commence:

Voici la fonction par défaut de l'interface:

Foo processFoo(foo, filters) {
// Should I null check foo and filters?

// Operations with foo.someFields
// Operations with filters.someFields

return foo; // If I null check then I should return some exception or null to the caller function. 
// If I don't null check just return received foo.
}

Implémentation :

default void someFunction() {

// instantiaded foo and filters
foo = processFoo(foo, filters);

// do some operations with foo
// null check if processFoo's contract returns null (description in the implementation)

}


13 commentaires

Ma règle de base est de ne jamais retourner un null de mes méthodes, mais un objet vide.


@zuckerburg Cela signifie-t-il que vous retournez un objet dans un état invalide au lieu de null?


@Torben Cela signifie créer quelque chose comme public static final Foo EMPTY = new Foo () et laisser la méthode retourner FOO .


juste un objet vide, donc si, par exemple, c'est une liste que j'itère sur l'appelant, cela n'échouera pas car il est nul, il n'a que 0 ligne. ou s'il s'agit d'un objet comme les détails de l'utilisateur, l'appelant retournera un objet vide, ce qui signifie que ce que vous voyez est un objet utilisateur vide, au lieu de null


Pour le meilleur ou pour le pire, le modèle d'objet nul n'est pas un idiome Java standard, bien qu'au moins "collection vide" et certains cas individuels similaires le soient.


Pour éviter les contrôles Null et NPE, une fonctionnalité facultative a été introduite. Jetez un œil à oracle.com/technetwork/articles/java/ …


Ce dont vous parlez comme une classe facultative en Java. Mais je pense que je parlais d'un scénario différent, il n'a pas besoin de retourner un nouveau Foo, cela peut aussi être une fonction void, la même question de vérification nulle existe toujours


Je pense que si vous avez le contrôle sur l'appel de méthode qui est processFoo (foo, filters), vous pouvez vous assurer en appelant que vous ne passez pas d'objet nul, ce qui vous facilitera la vie dans une autre méthode et vous ne finirez pas par faire contrôle nul.


duplication possible de [Qu'est-ce qu'une NullPointerException, et comment puis-je la corriger? ] ( stackoverflow.com/q/218384/4574765 )


La question que vous devriez vous poser est la suivante: ¿est-il logique de renvoyer un nul ici? ¿Que fera le code après, fonctionnera-t-il avec ce null ou sortira-t-il de la méthode? Ma règle est, s'il n'y a rien à voir avec cela, ne le vérifiez pas et attrapez simplement l'exception: de cette façon, vous pouvez facilement surveiller le problème. Si le programme peut et DEVRAIT continuer son travail même si l'objet est nul, ALORS vérifiez-le.


@zuckerburg Il me semble que vous avez pris la pratique légitime de la liste vide au lieu de zéro et l'avez étendue là où elle n'appartient pas. Le retour d'un objet empoty et invalide au lieu de null vous oblige toujours à faire la vérification. Si vous oubliez cette vérification (qui sera nécessairement plus compliquée que "== null"), vous passez l'objet invalide à une méthode qui n'autorise pas les valeurs nulles et l'erreur est potentiellement envoyée bien plus loin dans la pile d'appels (et rendue plus obscure ). Je déconseille fortement les objets vides (autres que les collections).


@Jai La comparaison d'une valeur de retour à une constante n'est pas différente de la comparaison à une valeur nulle. Pire encore, lorsqu'elle est transmise (par exemple en raison d'une erreur), la constante ne semble pas différente de l'objet passé intentionnellement, elle ne sera donc même pas interceptée par d'éventuelles annotations NonNull. Null est meilleur qu'un objet vide non valide car une méthode peut être documentée et annotée pour ne pas autoriser les valeurs nulles, puis rencontrer immédiatement une valeur nulle signifie une erreur de programmation involontaire.


@Torben Je sais que ce n'est pas un design merveilleux, mais plutôt ce que je pense que Zuckerburg voulait dire. Fait intéressant, JavaFX utilise cette conception pour certains (par exemple, Background.EMPTY ), mais probablement pas lié au contexte de la question.


3 Réponses :


4
votes

En règle générale, je ne vérifie jamais les valeurs nulles lorsque je contrôle les appels, mais si mon code peut être appelé par quelqu'un d'autre, je fais des vérifications, puisque vous parlez d'une spécification d'API, vous peut vérifier les paramètres d'entrée et lancer IllegalArgumentException.

Bien sûr, ce n'est pas obligatoire, la plupart du temps, lorsque vous avez un NPE, il y a un bogue sous-jacent quelque part, mais si vous ne contrôlez pas totalement les appels, vous aurez certainement besoin d'un effort supplémentaire. (surtout si votre API sera appelée fréquemment, vous avez besoin de quelque chose pour vous aider à retracer / identifier les mauvais modèles d'appels).

Lors de la conception d'une API, en plus de l'architecture elle-même, vous devez également vous concentrer sur la maintenance / le dépannage et la sécurité, surtout si l'API est exposée publiquement (ce qui signifie également nettoyer chaque paramètre).


0 commentaires

1
votes

Si vous avez implémenté la superposition dans votre projet, le bon endroit pour effectuer des vérifications nulles sont les couches qui reçoivent les données en externe. Comme par exemple: les contrôleurs, car il reçoit des données de l'utilisateur ... ou les passerelles car il reçoit des données de référentiels.

Mais si vous avez la possibilité de contrôler les valeurs de retour de votre méthode pour ne pas renvoyer null , ce serait bien. Cela vous permettra d'écrire du code avec moins de vérifications nulles, car vous pouvez simplement "autrucher" votre chemin à travers ces valeurs manquantes. Java nous a fourni des classes / méthodes telles que Optional , Reader.nullReader () de Java 11, Writer.nullWriter () et des collections vides depuis Collections.empty {List | Map | Set | ...} () .

Bien sûr, vous pouvez créer vos propres objets nuls afin qu'ils puissent se polymorphiser de manière à ne pas affecter le programme lorsqu'il est utilisé.


0 commentaires

1
votes

Je vais jouer l'avocat du diable ici parce qu'il semble que certaines personnes qui commentent semblent ne pas aimer le concept de «nul» ou l'utiliser dans leurs programmes.

«null» n'est pas quelque chose à éviter le moins du monde, et si vous essayez de le faire, vous serez vaincu; créer des objets et allouer de l'espace en mémoire pour de tels objets n'est pas nécessaire, car si votre code s'était cassé lors de la réception d'une valeur `` nulle '', il se casserait certainement également lors du traitement d'un objet qu'il ne peut tout simplement pas gérer. Non seulement cela, mais vous serez également vaincu en utilisant de nombreuses méthodes Java.

Si vous pouvez éviter d'utiliser "null" et donner à votre code quelque chose pour mieux travailler, faites-le. Sinon, ne vous cassez pas le dos et la colonne vertébrale pour éviter cela.


Enfin, pour répondre à votre question:

Vous entrez une vérification null lorsque vous n'êtes pas sûr si quelque chose, comme une méthode, peut renvoyer une valeur «nulle». Par exemple, si vous appelez une méthode que vous n'avez pas créée et que la méthode est conçue pour renvoyer un objet et qu'elle peut renvoyer une valeur nulle, ajoutez une vérification spéciale pour ce cas. Si vous savez que vous ne recevrez jamais une valeur nulle, comme si vous avez conçu vous-même une méthode de telle sorte qu'elle ne renvoie jamais «null», vous n'avez pas besoin de mettre de vérification nulle.

De plus, si vous n'utilisez que des méthodes internes ou privées et que vous savez que vous ne passez pas de paramètres nuls, comme dans votre exemple, il est également inutile d'ajouter des vérifications nulles. Cela s'applique également à l'instance de constructeur que vous avez mentionnée. Sinon, en règle générale dans les méthodes publiques, il est généralement bon d'utiliser des vérifications nulles, même si ce n'est que pour lancer une exception IllegalArgumentException ou pour renvoyer un échec.


2 commentaires

Lorsque vous n'êtes pas sûr que quelque chose pourrait renvoyer null, alors l'API est cassée et doit être corrigée afin que vous soyez sûr qu'elle peut renvoyer null. Ensuite ... lorsque vous êtes sûr qu'il peut renvoyer null, vous le documentez en renvoyant une référence d'objet facultative au lieu d'une référence d'objet directe, nullable.


Je ne parlais pas dans le cas de ne pas savoir ce qu'une méthode API pourrait renvoyer, mais plutôt dans le cas de la transmission de valeurs possibles à une méthode qui renverra null. Envisagez d'avoir une List contenant 'null' à l'index 4, et vous appelez list.get (4), renvoyant une valeur null pour les données que vous auriez pu espérer être non nulles. Dans ce cas, vous auriez besoin d'une vérification null car vous ne savez pas si la méthode renverra null. Cela n'implique pas une méthode cassée; cela implique une erreur inattendue qui doit être prise en compte. Utiliser une option, ou quelque chose de similaire, serait effectivement la même chose.