Est-il préférable d'utiliser assert ou IllegalArgumentException pour les paramètres de méthode requis?

88

En Java, qu'est-ce qui est le plus recommandé, et pourquoi? Les deux types jetteront des exceptions, de sorte que leur traitement est identique. assertest légèrement plus courte, mais je ne sais pas trop ce qui compte.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

contre

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}
Daenyth
la source
Je préfère un simple à la obj.hashCode()place ;-)
Marco

Réponses:

119

IL FAUT SE MÉFIER!

Les assertions sont supprimées au moment de l'exécution, à moins que vous ne spécifiiez explicitement d'activer les "assertions" lors de la compilation de votre code. Les assertions Java ne doivent pas être utilisées dans le code de production et doivent être limitées aux méthodes privées (voir Exception vs Assertion ), car les méthodes privées ne devraient être connues et utilisées que par les développeurs. En outre assertjetteront AssertionError qui Prolonge Errorpas Exception, et qui indique normalement vous avez une erreur très anormale (comme « OutOfMemoryError » qui est difficile à récupérer, non?) , Vous n'êtes pas censé être en mesure de traiter.

Supprimez l'indicateur "enable assertions" et vérifiez avec un débogueur et vous verrez que vous ne passerez pas à l'appel de levée IllegalArgumentException ... puisque ce code n'a pas été compilé (encore une fois, lorsque "ea" est supprimé).

Il est préférable d’utiliser la deuxième construction pour les méthodes publiques / protégées, et si vous voulez quelque chose qui est fait dans une ligne de code, il ya au moins une façon que je connaisse. Personnellement , j'utiliser le cadre du printemps de la Assertclasse qui a quelques méthodes de contrôle des arguments et que jeter « IllegalArgumentException » en cas d' échec. Fondamentalement, ce que vous faites est:

Assert.notNull(obj, "object was null");

... Ce qui exécutera en fait exactement le même code que celui que vous avez écrit dans votre deuxième exemple. Il existe quelques autres méthodes utiles telles que hasText, hasLengthici.

Je n'aime pas écrire plus de code que nécessaire, je suis donc content de réduire le nombre de lignes écrites de 2 (2 lignes> 1 ligne) :-)

Jalayn
la source
Ah, j'ai oublié de retirer des affirmations! Très bonne réponse. Je vais attendre un peu pour voir si quelque chose d'autre arrive, puis acceptez-le :)
Daenyth
2
Notez qu'aucun indicateur ne supprime les assertions au moment de la compilation (bien qu'elles puissent être supprimées via la compliation conditionnelle ). Les assertions sont désactivées par défaut à l'exécution (je pense que la machine virtuelle Java les traite comme des NOOP), mais peuvent être activées via java -eaet par programme. @Jalayn Je pense que les assertions dans le code de production sont parfaitement valables, car elles sont utiles pour le débogage sur le terrain
Justin Muller le
@Jalayn, -1. Le compilateur ne supprime pas le code d'assertion. Même s'ils ne seront pas exécutés à moins que vous ne le fassiez java -ea.
Pacerier
5
pas besoin du framework Spring quand on peut l'utiliserObjects.requreNonNull
cambunctious
46

Vous devez utiliser une exception. L'utilisation d'une assertion constituerait un abus de la fonctionnalité.

Les exceptions non contrôlées sont conçues pour détecter les erreurs de programmation des utilisateurs de votre bibliothèque, tandis que les assertions sont conçues pour détecter les erreurs dans votre propre logique. Ce sont des questions distinctes qui ne devraient pas être mélangées.

Par exemple, une assertion

assert myConnection.isConnected();

signifie "Je sais que chaque chemin de code menant à cette assertion garantit que la myConnectionconnexion est établie; si le code ci-dessus ne parvient pas à obtenir une connexion valide, il aurait dû déclencher une exception ou un retour avant d'atteindre ce point."

D'autre part, un chèque

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

signifie que "Appeler ma bibliothèque sans établir de connexion est une erreur de programmation".

dasblinkenlight
la source
1
Cette information est vraiment utile, mais j'accepte Jalayn car elle indique comment je pourrais potentiellement introduire un bogue avec la méthode assert.
Daenyth
4
excellent point "Les exceptions non vérifiées sont conçues pour détecter les erreurs de programmation des utilisateurs de votre bibliothèque, alors que les assertions sont conçues pour détecter les erreurs dans votre propre logique. Ce sont des problèmes distincts qui ne doivent pas être mélangés."
Asim Ghaffar le
C'est vrai, mais cela suppose que OP est en train d'écrire une bibliothèque. Si leur code est juste pour usage interne, une assertion est acceptable.
user949300
11

Si vous écrivez une fonction qui n'autorise pas nullune valeur de paramètre valide, vous devez ajouter l' @Nonnullannotation à la signature et l'utiliser Objects.requireNonNullpour vérifier si l'argument est nullet afficher un NullPointerExceptionsi. L' @Nonnullannotation est pour la documentation et fournira des avertissements utiles lors de la compilation dans certains cas. Cela n'empêche pas nulld'être transmis au moment de l'exécution.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Mise à jour: L' @Nonnullannotation ne fait pas partie de la bibliothèque standard Java. Au lieu de cela, il existe de nombreux standards concurrents de bibliothèques tierces (voir Quelle annotation Java @NotNull dois-je utiliser? ). Cela ne signifie pas que c'est une mauvaise idée de l'utiliser, mais que ce n'est pas standard.

cambunctious
la source
Merci Objects.requireNonNull()de me l' avoir signalé , ce qui était nouveau pour moi. Savez-vous s'il existe une méthode similaire "requireTrue ()" ou "requireEqual ()" n'importe où? Rien contre Spring's Assert mais tout le monde ne l'utilise pas.
user949300
@ user949300 Objects.requireNonNull()sert à la validation des arguments. Si un argument doit être trueégal ou égal à quelque chose, alors l'argument est inutile. Pour les cas d'erreur autres que des arguments illégaux, vous devriez throwun Exceptionqui décrit plus précisément l'erreur. Il y a aussi JUnit Assertmais c'est pour les tests.
cambunctious
Je pensais plutôt à, disons, pour une fonction racine carrée Objects.requireTrue(x >= 0.0);, ou pour un hachage quelconqueObjects.requireEquals(hash.length == 256)
user949300
Quel @Nonnull je dois utiliser? javax.validation.constraints.NotNull?
Aguid
J'utiliserais les annotations Eclipse JDT , car elles ont été créées par des malins :). Documentation: help.eclipse.org/neon/… - Vous pouvez également configurer IntelliJ pour les utiliser.
Koppor
2

Je préfère toujours lancer l'exception IllegalArgumentException sur les assertions.

Les assertions sont principalement utilisées dans JUnit ou d'autres outils de test afin de vérifier / assert des résultats de test. Cela pourrait donc donner une fausse impression aux autres développeurs que votre méthode est une méthode de test.

De plus, il est logique de lancer IllegalArgumentException lorsqu'une méthode a été transmise à un argument illégal ou inapproprié . Ceci est plus cohérent avec la convention de gestion des exceptions suivie par les développeurs Java.

rai.skumar
la source
5
Les affirmations remontaient à environ 40 ans avant JUnit - interrogez un programmeur C sur les macros ASSERT.
JBRWilkinson
3
cette question ne concerne pas c; c'est à propos de Java. J'ai donc répondu dans le contexte de Java.
rai.skumar
Ne confondez pas assert (le mot clé réservé) et Assert (la classe JUnit). Ils sont tous deux utilisés pour effectuer une vérification d'une variable, mais ils sont deux choses très différentes et se comportent très différemment.
Début novembre
1

La deuxième solution IMO est légèrement meilleure, car elle apporte davantage d'informations et pourrait être encore étendue (par exemple en élargissant la classe des exceptions) pour être encore plus informative. De plus, elle n'utilise pas de comparaison négative plus facile à comprendre.

0lukasz0
la source
1

Je n'utilise pas beaucoup d'aserts, mais une approche commune avec Lombock @NonNull: https://projectlombok.org/features/NonNull

Implémentation de Lombok: import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Version Java:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok est vraiment une très belle bibliothèque que j'utilise partout

Kensai
la source