115 votes

Quand une exception IllegalArgumentException doit-elle être levée ?

Je crains qu'il s'agisse d'une exception d'exécution et qu'il faille donc l'utiliser avec parcimonie.
Cas d'utilisation standard :

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Mais cela semble forcer la conception suivante :

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

Pour qu'il redevienne une exception vérifiée.

Ok, mais allons-y avec ça. Si vous donnez une mauvaise entrée, vous obtenez une erreur d'exécution. Tout d'abord, il s'agit d'une politique assez difficile à mettre en œuvre de manière uniforme, car vous pourriez avoir à faire la conversion inverse :

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

Et pire, en vérifiant 0 <= pct && pct <= 100 Ce n'est pas le cas pour des données plus avancées, telles qu'une adresse électronique, ou pire, quelque chose qui doit être vérifié par rapport à une base de données, donc en général le code client ne peut pas effectuer de pré-validation.

Donc, en gros, ce que je dis c'est que je ne vois pas de politique cohérente et significative pour l'utilisation de IllegalArgumentException . Il semble qu'elle ne devrait pas être utilisée et que nous devrions nous en tenir à nos propres exceptions vérifiées. Quel est le bon cas d'utilisation pour lancer cette exception ?

91voto

Nathan Hughes Points 30377

Le document API pour IllegalArgumentException :

Lancé pour indiquer qu'un argument illégal ou inapproprié a été transmis à une méthode.

En regardant comment il est utilisé dans les bibliothèques du JDK je dirais :

  • Il semble que ce soit une mesure défensive de se plaindre d'une entrée manifestement mauvaise avant que l'entrée ne puisse s'immiscer dans le travail et faire échouer quelque chose à mi-chemin avec un message d'erreur absurde.

  • Il est utilisé pour les cas où il serait trop ennuyeux de lancer une exception vérifiée (bien qu'il fasse une apparition dans le code java.lang.reflect, où le souci de niveaux ridicules de lancement d'exceptions vérifiées n'est pas autrement apparent).

J'utiliserais IllegalArgumentException pour vérifier les arguments défensifs de dernière minute pour les utilitaires communs (en essayant de rester cohérent avec l'utilisation du JDK). Ou lorsque l'on s'attend à ce qu'un mauvais argument soit une erreur du programmeur, comme dans le cas d'un NullPointerException . Je ne l'utiliserais pas pour mettre en œuvre la validation dans un code commercial. Je ne l'utiliserais certainement pas pour l'exemple de l'e-mail.

9 votes

Je pense que le conseil "où l'on s'attend à ce qu'un mauvais argument soit une erreur du programmeur" est le plus cohérent avec la façon dont j'ai vu cette utilisation, donc j'accepte cette réponse.

26voto

Tom Points 693

Lorsque l'on parle de "mauvaise entrée", il faut tenir compte de l'origine de l'entrée.

Si l'entrée est saisie par un utilisateur ou un autre système externe que vous ne contrôlez pas, vous devez vous attendre à ce que l'entrée soit invalide et toujours la valider. Il est tout à fait acceptable de lancer une exception vérifiée dans ce cas. Votre application doit " récupérer " cette exception en fournissant un message d'erreur à l'utilisateur.

Si l'entrée provient de votre propre système, par exemple votre base de données, ou d'autres parties de votre application, vous devriez pouvoir compter sur sa validité (elle devrait avoir été validée avant d'arriver là). Dans ce cas, il est tout à fait acceptable de lancer une exception non vérifiée, comme une IllegalArgumentException, qui ne doit pas être attrapée (en général, vous ne devez jamais attraper les exceptions non vérifiées). C'est une erreur du programmeur que la valeur invalide soit arrivée là en premier lieu ;) Vous devez la corriger.

4 votes

Pourquoi "vous ne devez jamais attraper les exceptions non vérifiées" ?

12 votes

Parce qu'une exception non vérifiée est censée être levée à la suite d'une erreur de programmation. On ne peut pas raisonnablement s'attendre à ce que l'appelant d'une méthode lançant de telles exceptions puisse s'en remettre, et il est donc généralement inutile de les attraper.

3 votes

Because an unchecked exception is meant to be thrown as a result of a programming error m'a aidé à clarifier beaucoup de choses dans ma tête, merci :)

17voto

Louis Wasserman Points 67557

Lancer des exceptions d'exécution "avec parcimonie" n'est pas vraiment une bonne politique -- Effective Java recommande d'utiliser des exceptions vérifiées lorsque on peut raisonnablement s'attendre à ce que l'appelant récupère . (L'erreur de programmeur est un exemple spécifique : si un cas particulier indique une erreur de programmeur, alors vous devez lancer une exception non vérifiée ; vous voulez que le programmeur ait une trace de pile de l'endroit où le problème de logique s'est produit, et non qu'il essaie de le gérer lui-même).

S'il n'y a aucun espoir de récupération, alors n'hésitez pas à utiliser des exceptions non vérifiées ; il n'y a aucun intérêt à les attraper, donc c'est tout à fait normal.

Votre exemple ne permet pas de déterminer clairement dans quel cas il s'agit de votre code, cependant.

0 votes

Je pense que "dont on peut raisonnablement espérer qu'il se rétablira" est une expression malveillante. Toute opération foo(data) pourrait avoir eu lieu dans le cadre de for(Data data : list) foo(data); dans lequel l'appelant pourrait vouloir que le plus grand nombre possible d'entre eux réussissent, même si certaines données sont malformées. Cela inclut également les erreurs de programmation. Si l'échec de mon application signifie qu'une transaction n'aboutira pas, c'est probablement mieux, mais si cela signifie que le refroidissement nucléaire s'arrête, c'est mauvais.

0 votes

StackOverflowError et ce sont des cas dont on ne peut raisonnablement attendre de l'appelant qu'il se remette. Mais il semble que tout cas de données ou de niveau logique d'application devrait être vérifié. Cela signifie qu'il faut vérifier les pointeurs nuls !

5 votes

Dans une application de refroidissement nucléaire, je préférerais échouer aux tests plutôt que de permettre à un cas que le programmeur pensait impossible de passer inaperçu.

7voto

djechlin Points 18051

Traiter IllegalArgumentException en tant que conditions préalables vérifier, et considérer le principe de conception : Une méthode publique doit à la fois connaître et documenter publiquement ses propres conditions préalables.

Je suis d'accord pour dire que cet exemple est correct :

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Si EmailUtil est opaque Si les conditions préalables ne peuvent pas être décrites à l'utilisateur final pour une raison quelconque, une exception vérifiée est correcte. La deuxième version, corrigée pour cette conception :

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

Si EmailUtil est transparent Par exemple, il peut s'agir d'une méthode privée appartenant à la classe en question, IllegalArgumentException est correcte si et seulement si ses préconditions peuvent être décrites dans la documentation de la fonction. Cette version est également correcte :

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

Ce design pourrait aller dans les deux sens.

  • Si les conditions préalables sont coûteuses à décrire, ou si la classe est destinée à être utilisée par des clients qui ne savent pas si leurs courriels sont valides, alors utilisez la méthode suivante ParseException . La méthode de premier niveau ici est nommée scanEmail ce qui laisse entendre que l'utilisateur final a l'intention d'envoyer des emails non étudiés, donc c'est probablement correct.
  • Si les conditions préalables peuvent être décrites dans la documentation de la fonction, et si la classe n'a pas l'intention de prendre en compte les entrées invalides et que, par conséquent, une erreur du programmeur est indiquée, utilisez IllegalArgumentException . Bien qu'il ne soit pas "vérifié", le "contrôle" se déplace vers le Javadoc documentant la fonction, que le client est censé respecter. IllegalArgumentException où le client ne peut pas savoir à l'avance que son argument est illégal, c'est faux.

Une note sur IllegalStateException : Cela signifie que "l'état interne de cet objet (variables d'instance privées) n'est pas en mesure d'effectuer cette action." L'utilisateur final ne peut pas voir l'état privé, donc, au sens large, il est prioritaire par rapport à IllegalArgumentException dans le cas où l'appel du client n'a aucun moyen de savoir que l'état de l'objet est incohérent. Je n'ai pas de bonne explication quant à la préférence à accorder aux exceptions vérifiées, bien que des choses comme l'initialisation double ou la perte d'une connexion à une base de données qui n'est pas récupérée en soient des exemples.

6voto

Vishal K Points 9232

Comme spécifié dans le tutoriel officiel d'oracle, il est indiqué que :

Si un client peut raisonnablement s'attendre à récupérer d'une exception, faites-en une exception vérifiée. Si un client ne peut rien faire pour récupérer l'exception, il s'agit d'une exception non vérifiée.

Si j'ai une application qui interagit avec la base de données à l'aide de JDBC Et j'ai une méthode qui prend l'argument comme étant le int item y double price . Le site price pour l'élément correspondant est lu à partir de la table de la base de données. Je multiplie simplement le nombre total de item acheté avec le price et renvoie le résultat. Bien que je sois toujours sûr, du côté de l'application, que la valeur du champ de prix dans la table ne peut jamais être négative, que se passe-t-il si la valeur du prix sort de la table ? negativo ? Cela montre qu'il y a un sérieux problème du côté de la base de données. Peut-être une mauvaise saisie du prix par l'opérateur. C'est le genre de problème que l'autre partie de l'application qui appelle cette méthode ne peut pas anticiper et ne peut pas récupérer. Il s'agit d'un BUG dans votre base de données. Donc, et IllegalArguementException() devrait être lancée dans ce cas, ce qui indiquerait que the price can't be negative .
J'espère que j'ai exprimé mon point de vue clairement

0 votes

Je n'aime pas ce conseil (d'Oracle) parce que la gestion des exceptions consiste à savoir comment récupérer, et non à savoir si l'on doit récupérer. Par exemple, une requête malformée d'un utilisateur ne vaut pas la peine de faire planter un serveur web entier.

0 votes

@djechlin . . . Parfois, il n'est pas possible pour l'application de se rétablir. C'est souvent le cas lorsque des arguments illégaux sont passés. C'est quelque chose que l'appelant doit corriger au lieu de l'appelé. C'est là que les exceptions d'exécution entrent en jeu. En ce qui concerne votre exemple, je suis d'accord pour dire qu'une requête utilisateur malformée ne devrait pas faire planter un serveur web entier. Mais je ne considère pas qu'une requête utilisateur malformée soit un argument invalide en premier lieu. Le serveur web devrait analyser brièvement la requête invalide et renvoyer gentiment cette ordure à l'appelant (éventuellement avec des indications sur la façon de la corriger). Le problème est résolu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X