Question

Je suis dans le milieu de la lecture de l'excellent Code Propre

Une discussion concernant la transmission des valeurs null dans une méthode.

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}
...
calculator.xProjection(null, new Point(12,13));

Il représente les différentes façons de traiter ce:

public double xProjection(Point p1, Point p2) {
    if (p1 == null || p2 == null) {
        throw new IllegalArgumentException("Invalid argument for xProjection");
    }
    return (p2.x - p1.x) * 1.5;
}

public double xProjection(Point p1, Point p2) {
    assert p1 != null : "p1 should not be null";
    assert p2 != null : "p2 should not be null";
    return (p2.x - p1.x) * 1.5;
}

Je préfère l' les assertions approche, mais je n'aime pas le fait que les assertions sont désactivés par défaut.

Le livre enfin les états:

Dans la plupart des langages de programmation il n'y a pas de bonne façon de traiter avec une valeur null qui est transmis par un appelant accidentellement.Parce que c'est le cas, l'approche rationnelle est d'interdire passage de la valeur null par défaut.

Il n'a pas vraiment comment vous le feriez appliquer cette restriction?

N'importe quel d'entre vous ont des opinions fortes de toute façon.

Était-ce utile?

La solution

L'utilisation d'affirmations et de lancer des exceptions sont des approches valables ici.Soit le mécanisme peut être utilisé pour indiquer une erreur de programmation, pas une erreur d'exécution, comme c'est le cas ici.

  • Affirmations ont l'avantage de performance qu'ils sont désactivés sur les systèmes de production.
  • Des Exceptions ont l'avantage de la sécurité, ainsi que la vérification est toujours effectuée.

Le choix dépend vraiment de l'pratiques en matière de développement du projet.L'ensemble du projet doit décider sur une affirmation politique:si le choix est de permettre à des affirmations cours de développement, je dirais que pour utiliser les assertions de vérifier ce genre de paramètre non valide - dans un système de production, une NullPointerException levée en raison d'une erreur de programmation est peu probable d'être en mesure d'être capturées et traitées de manière significative, de toute façon et donc va agir comme une affirmation.

Pratiquement, si, je sais que beaucoup de développeurs qui n'ont pas confiance, que les assertions sera activée lorsque cela est approprié et ainsi opter pour la sécurité de lancer une exception NullPointerException.

Bien sûr, si vous ne pouvez pas appliquer une politique de votre code (si vous êtes en train de créer une bibliothèque, par exemple, et donc dépendent de la façon dont d'autres développeurs d'exécuter votre code), vous devez opter pour l'approche sécuritaire de jeter NullPointerException pour ces méthodes qui font partie de la bibliothèque de l'API.

Autres conseils

Règle générale, si votre méthode ne vous attendez pas null arguments, alors vous devriez jeter Système.ArgumentNullException.Jetant bon Exception non seulement vous protège des ressources de la corruption et d'autres mauvaises choses, mais sert comme un guide pour les utilisateurs de votre code d'enregistrement du temps passé dans le débogage de votre code.

Lire également un article sur Une programmation défensive

Aussi pas d'usage immédiat, mais liées à la mention de Spec#...Il y a une proposition visant à ajouter "null-sûr type" pour une future version de Java: "Améliorée de la gestion des valeurs null - Null-safe types".

En vertu de la proposition, votre méthode pourrait devenir

public class MetricsCalculator {
    public double xProjection(#Point p1, #Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}

#Point est le type de non-null les références à des objets de type Point.

Il n'a pas vraiment comment vous le feriez appliquer cette restriction?

Vous l'appliquer en jetant un ArgumentExcexception si ils passent dans la valeur null.

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}

Je préfère l'utilisation d'affirmations.

J'ai une règle que je n'utilise que des assertions contenues dans le public et protégé méthodes.C'est parce que je crois que la méthode d'appel doit s'assurer qu'il est en train de passer des arguments valables pour des méthodes privées.

Spec# a l'air très intéressant!

Quand quelque chose comme ce qui n'est pas disponible, en général, je test non-méthodes privées avec un null-case, et les revendications des méthodes internes.Plutôt que de code le nul de vérifier explicitement dans chaque méthode, j'ai délégué à une classe d'utilitaires avec un chèque null méthode:

/**
 * Checks to see if an object is null, and if so 
 * generates an IllegalArgumentException with a fitting message.
 * 
 * @param o The object to check against null.
 * @param name The name of the object, used to format the exception message
 *
 * @throws IllegalArgumentException if o is null.
 */
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException {
   if (null == o)
      throw new IllegalArgumentException(name + " must not be null");
}

public static void checkNull(Object o) throws IllegalArgumentException {
   checkNull(o, "object");
} 

// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
   for(Object o in os) checkNull(o);  
}

Alors, le contrôle se transforme en:

public void someFun(String val1, String val2) throws IllegalArgumentException {
   ExceptionUtilities.checkNull(val1, "val1");
   ExceptionUtilities.checkNull(val2, "val2");

   /** alternatively:
   ExceptionUtilities.checkNull(val1, val2);
   **/

   /** ... **/
} 

Que peuvent être ajoutées à l'aide de l'éditeur de macros, ou un code-script de traitement.Edit: L'détaillé de vérification pourraient être ajoutés de cette manière, mais je pense que c'est nettement plus facile à automatiser l'ajout d'une seule ligne.

Dans la plupart des langages de programmation il n'y a pas de bonne façon de traiter avec une valeur null qui est transmis par un appelant accidentellement.Parce que c'est le cas, l'approche rationnelle est d'interdire passage de la valeur null par défaut.

J'ai trouvé JetBrains' @Nullable et @NotNull annotations approche pour faire face à cette les plus ingénieux, jusqu'à présent.C'est de l'IDE spécifique, malheureusement, mais très propre et puissant, de l'OMI.

http://www.jetbrains.com/idea/documentation/howto.html

Avoir cette (ou quelque chose de similaire) comme un standard java serait vraiment sympa.

Bien qu'il n'est pas strictement lié, vous voudrez peut-être jeter un oeil à Spec#.

Je pense qu'il est encore en développement (par Microsoft), mais certains CTP sont disponibles et il semble prometteur.Fondamentalement, il vous permet de le faire:

  public static int Divide(int x, int y)
    requires y != 0 otherwise ArgumentException; 
  {
  }

ou

  public static int Subtract(int x, int y)
    requires x > y;
    ensures result > y;
  {
    return x - y;
  } 

Il fournit également une autre des caractéristiques comme Notnull types.Il est construit sur le sommet de la .NET Framework 2.0 et il est entièrement compatible.Le syntaxt, comme vous pouvez le voir, est C#.

@Chris Karcher, je dirais tout à fait correcte.La seule chose que je dirais est de vérifier les paramètres séparément et ont l'exeption rapport au param c'était nul aussi car il rend le suivi de où la valeur null est à venir à partir de beaucoup plus facile.

@wvdschel wow!Si l'écriture du code, c'est trop d'effort pour vous, vous devriez regarder dans quelque chose comme PostSharp (ou Java équivalent s'il est disponible) qui peut post-traiter vos assemblées et insérez param vérifie pour vous.

Depuis le hors-sujet semble être devenu le sujet, Scala prend une approche intéressante pour cela.Tous les types sont supposés être non nulle, sauf si vous explicitement l'envelopper dans un Option pour indiquer qu'il peut être null.Donc:

//  allocate null
var name : Option[String]
name = None

//  allocate a value
name = Any["Hello"]

//  print the value if we can
name match {
  Any[x] => print x
  _ => print "Nothing at all"
}

En général, je préfère ne pas le faire, puisque c'est seulement ralentir les choses.NullPointerExceptions sont jetés plus tard, de toute façon, ce qui conduira rapidement à l'utilisateur de découvrir qu'ils sont en passant null à la méthode.J'ai utilisé pour vérifier, mais 40% de mon code a fini par être le code de vérification, à quel point j'ai décidé qu'il était juste pas la peine de la belle affirmation des messages.

Je suis d'accord ou pas d'accord avec wvdschel post, cela dépend de ce qu'il est précisément en train de dire.

Dans ce cas, bien sûr, cette méthode se crash sur null si la vérification explicite ici n'est probablement pas nécessaire.

Cependant, si la méthode stocke simplement les données passées, et il y a une autre méthode que vous appelez plus tard, qui va traiter avec elle, la découverte de la mauvaise entrée le plus tôt possible est la clé de la résolution de bugs plus rapidement.À ce moment plus tard, il pourrait y avoir une multitude de façons que les données de mauvaise qualité qui est arrivé à être donné à votre classe.C'est en essayant de comprendre comment les rats sont venus dans votre maison après le fait, en essayant de trouver le trou quelque part.

Légèrement hors-sujet, mais une des caractéristiques de findbugs qui je pense est très utile, c'est d'être capable d'annoter les paramètres de méthodes pour décrire les paramètres ne doivent pas être passé à une valeur nulle.

À l'aide de l'analyse statique de code, findbugs peuvent ensuite le point des endroits où la méthode est appelée avec potentiellement une valeur null.

Cela a deux avantages:

  1. L'annotation décrit votre intention pour la façon dont la méthode doit être appelée, l'aide de la documentation
  2. FindBugs peut point à problème potentiel appelants de la méthode, vous permettant de traquer les éventuels bugs.

Utile seulement si vous avez accès au code qui appelle vos méthodes, mais c'est habituellement le cas.

Thwrowing C# ArgumentException, ou Java IllegalArgumentException dès le début de la méthode me semble que le plus clair de solutions.

Il faut toujours être prudent avec les Exceptions d'Exécution - les exceptions qui ne sont pas déclarés à la signature de la méthode.Puisque le compilateur ne les appliquent pas vous attraper ces il est vraiment facile de les oublier.Assurez-vous que vous avez une sorte de "catch all" gestion des exceptions pour éviter que le logiciel d'arrêter brusquement.C'est la partie la plus importante de votre expérience utilisateur.

La meilleure façon de traiter ce serait vraiment l'utilisation d'exceptions.En fin de compte, l'affirme vont finir par donner un similaire l'expérience de l'utilisateur final, mais sans préciser de façon pour le développeur d'appeler votre code pour gérer la situation, avant de montrer une exception à l'utilisateur final.Ultimatley, vous voulez vous assurer que vous tester pour les entrées invalides dès que possible (surtout en public en face de code) et de fournir les exceptions appropriées que le code appelant peut l'attraper.

Dans une Java façon, en supposant que la valeur null vient d'une erreur de programmation (ie.ne doit jamais aller à l'extérieur de la phase de test), puis laissez le système de le jeter, ou si il y a des effets secondaires atteindre ce point, vérifier la valeur null au début et à la jeter, soit IllegalArgumentException ou NullPointerException.

Si la valeur null peut venir d'une réelle exceptional affaire, mais vous ne voulez pas utiliser un checked exception pour que, alors vous avez certainement envie d'aller le IllegalArgumentException route au début de la méthode.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top