Style de constructeur Java: les parameters de vérification ne sont pas nuls

Quelles sont les meilleures pratiques si vous avez une classe qui accepte certains parameters mais qu’aucun d’eux n’est autorisé à être null ?

Ce qui suit est évident mais l’exception est un peu non spécifique:

 public class SomeClass { public SomeClass(Object one, Object two) { if (one == null || two == null) { throw new IllegalArgumentException("Parameters can't be null"); } //... } } 

Ici, les exceptions vous permettent de savoir quel paramètre est nul, mais le constructeur est maintenant plutôt moche:

 public class SomeClass { public SomeClass(Object one, Object two) { if (one == null) { throw new IllegalArgumentException("one can't be null"); } if (two == null) { throw new IllegalArgumentException("two can't be null"); } //... } 

Ici, le constructeur est plus propre, mais maintenant le code constructeur n’est pas vraiment dans le constructeur:

 public class SomeClass { public SomeClass(Object one, Object two) { setOne(one); setTwo(two); } public void setOne(Object one) { if (one == null) { throw new IllegalArgumentException("one can't be null"); } //... } public void setTwo(Object two) { if (two == null) { throw new IllegalArgumentException("two can't be null"); } //... } } 

Lequel de ces styles est le meilleur?

Ou existe-t-il une alternative plus largement acceptée?

Le deuxième ou le troisième.

Parce qu’il indique à l’utilisateur de votre API ce qui s’est mal passé.

Pour moins de verbosité, utilisez Validate.notNull(obj, message) partir de commons-lang. Ainsi, votre constructeur ressemblera à:

 public SomeClass(Object one, Object two) { Validate.notNull(one, "one can't be null"); Validate.notNull(two, "two can't be null"); ... } 

Placer le chèque dans le setter est également acceptable, avec le même commentaire de verbosité. Si vos créateurs ont également pour rôle de préserver la cohérence des objects, vous pouvez également choisir le troisième.

Vous pouvez utiliser l’une des nombreuses bibliothèques conçues pour faciliter les contrôles de pré-conditionnement. Beaucoup de code dans Google Guava utilise com.google.common.base.Preconditions

Méthodes statiques simples à appeler au début de vos propres méthodes pour vérifier les arguments et l’état corrects. Cela permet des constructions telles que

  if (count <= 0) { throw new IllegalArgumentException("must be positive: " + count); } 

être remplacé par le plus compact

  checkArgument(count > 0, "must be positive: %s", count); 

Il a checkNotNull qui est largement utilisé dans Guava . Vous pouvez alors écrire:

  import static com.google.common.base.Preconditions.checkNotNull; //... public SomeClass(Object one, Object two) { this.one = checkNotNull(one); this.two = checkNotNull(two, "two can't be null!"); //... } 

La plupart des méthodes sont surchargées pour ne prendre aucun message d'erreur, un message d'erreur fixe ou un message d'erreur modélisé avec varargs.


Sur IllegalArgumentException vs NullPointerException

Alors que votre code d'origine lève une IllegalArgumentException sur null arguments null , IllegalArgumentException de Guava lève plutôt NullPointerException .

Voici une citation de Effective Java 2nd Edition: Article 60: Favoriser l'utilisation des exceptions standard :

Sans doute, toutes les invocations de méthode erronées se résument à un argument illégal ou à un état illégal, mais d'autres exceptions sont généralement utilisées pour certains types d'arguments et d'états illégaux. Si un appelant passe la valeur null dans un paramètre pour lequel les valeurs NULL sont interdites, la convention stipule que NullPointerException être levée au lieu d' IllegalArgumentException .

Une NullPointerException n'est pas réservée uniquement lorsque vous accédez aux membres d'une référence null ; c'est assez standard de les lancer quand un argument est null quand c'est une valeur illégale.

 System.out.println("some ssortingng".split(null)); // throws NullPointerException 

Vieille question; une autre nouvelle réponse (déjà mentionnée par un autre commentaire, mais je pense que cela vaut sa propre réponse).

Java 7 a ajouté java.lang.Objects.requireNonNull() aux API que tout le monde peut utiliser. Donc, vérifier tous les arguments pour null revient à une liste courte comme:

 this.arg1 = Objects.requireNonNull(arg1, "arg1 must not be null"); this.arg2 = Objects.requireNonNull(arg2, "arg2 must not be null"); 

Notes annexes:

  • assurez-vous de ne pas inverser les deux arguments – le second est le message qui sera utilisé pour le NPE qui est lancé si le premier argument est null (si vous les inversez, eh bien, votre contrôle ne sera jamais échoué)
  • une autre meilleure pratique: si possible, faites en sorte que tous les membres de votre classe soient finaux (vous pouvez donc être sûr: quand un object a été créé avec succès, tous ses membres ne sont pas nuls; ils ne changeront pas avec le temps)

J’aurais une méthode utilitaire:

  public static  T checkNull(Ssortingng message, T object) { if(object == null) { throw new NullPointerException(message); } return object; } 

Je voudrais qu’il retourne l’object afin que vous puissiez l’utiliser dans des tâches comme ceci:

  public Constructor(Object param) { this.param = checkNull("Param not allowed to be null", param); } 

EDIT: En ce qui concerne les suggestions d’utilisation d’une bibliothèque tierce, les conditions préalables de Google en particulier sont encore meilleures que mon code. Cependant, si c’est la seule raison d’inclure la bibliothèque dans votre projet, je serais hésitant. La méthode est trop simple.

Hormis les réponses données ci-dessus qui sont toutes valables et raisonnables, je pense qu’il est bon de souligner que vérifier la présence de null n’est peut-être pas une “bonne pratique”. (En supposant que les lecteurs autres que l’OP puissent considérer la question comme dogmatique)

Du blog de Misko Hevery sur la testabilité: affirmer ou ne pas affirmer

Comparaison des moyens de vérifier les conditions préalables en Java – Gava vs. Apache Commons vs Spring Framework vs Plain Java Asserts

 public static void fooSpringFrameworkAssert(Ssortingng name, int start, int end) { // Preconditions Assert.notNull(name, "Name must not be null"); Assert.isTrue(start < end, "Start (" + start + ") must be smaller than end (" + end + ")"); // Do something here ... } public static void fooApacheCommonsValidate(String name, int start, int end) { // Preconditions Validate.notNull(name, "Name must not be null"); Validate.isTrue(start < end, "Start (%s) must be smaller than end (%s)", start, end); // Do something here ... } public static void fooGuavaPreconditions(String name, int start, int end) { // Preconditions Preconditions.checkNotNull(name, "Name must not be null"); Preconditions.checkArgument(start < end, "Start (%s) must be smaller than end (%s)", start, end); // Do something here ... } public static void fooPlainJavaAsserts(String name, int start, int end) { // Preconditions assert null != name : "Name must not be null"; assert start < end : "Start (" + start + ") must be smaller than end (" + end + ")"; // Do something here ... } 

Ceci est un résumé de cet article: http://www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-java

Une alternative à lancer une exception non vérifiée serait l’utilisation d’ assert . Sinon, je lancerais des exceptions vérifiées pour que l’appelant sache que le constructeur ne fonctionnera pas avec des valeurs illégales.

La différence entre vos deux premières solutions – avez-vous besoin d’un message d’erreur détaillé, avez-vous besoin de savoir quel paramètre a échoué ou est-ce suffisant pour savoir que l’instance n’a pas pu être créée à cause d’arguments illégaux?

Notez que les deuxième et troisième exemples ne peuvent pas signaler correctement que les deux parameters ont été nuls.

BTW – je vote pour une variation de (1):

 if (one == null || two == null) { throw new IllegalArgumentException( Ssortingng.format("Parameters can't be null: one=%s, two=%s", one, two)); } 

Les annotations pour l’parsing statique sont également utiles, que ce soit en complément ou à la place des contrôles d’exécution.

FindBugs, par exemple, fournit une annotation @NonNull.

public SomeClass (object One Object @NonNull, object deux @NonNull) {

Vous pouvez simplement avoir une méthode qui prend tous les arguments du constructeur que vous devez valider. Cette méthode renvoie une exception avec un message spécifique en fonction de l’argument non valide. Votre constructeur appelle cette méthode et, si elle passe, elle initialise les valeurs.

Je suppose que vous parlez de l’assertion intégrée en Java. À mon avis, ce n’est pas vraiment une bonne idée de l’utiliser. Comme il peut être activé / désactivé à l’aide des parameters de ligne de commande. Par conséquent, certains disent qu’il est seulement acceptable d’utiliser des méthodes privées.

Mes mentors me disent de ne pas réinventer la roue! Leur conseil est d’utiliser des bibliothèques. Ils sont (probablement) bien conçus et testés. Bien sûr, il est de votre responsabilité de vous assurer que vous prenez une bibliothèque de bonne qualité.

D’autres me disent que le ppl Enterprise – dans certains termes – est erroné et que vous introduisez plus de dépendance – pour des tâches simples – que nécessaire. Je peux accepter ce point aussi … Mais voici ma dernière expérience:

J’ai d’abord écrit ma propre méthode privée pour vérifier les parameters nuls. C’est ennuyeux et redondant. Je sais que je devrais le mettre dans une classe Utility. Mais pourquoi devrais-je l’écrire en premier lieu, quand quelqu’un l’a déjà fait? Je peux gagner du temps en écrivant des tests unitaires et en concevant des éléments existants. À moins que vous ne vouliez faire de l’exercice ou apprendre, je ne le recommanderais pas.

J’ai récemment commencé à utiliser la goyave de Google et je trouve que – avec apache commons – une fois que vous commencez à les utiliser, vous ne les utiliserez que pour cette seule méthode. Vous allez le découvrir et l’utiliser de plus en plus. À la fin, cela rendra votre code plus court, plus lisible, plus cohérent et plus facile à entretenir.

BTW .: Selon vos objectives, j’irais avec 2 ou 3 en utilisant une des bibliothèques mentionnées ci-dessus …