web-dev-qa-db-fra.com

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

Je crains qu'il s'agisse d'une exception à l'exécution, elle devrait donc être utilisée avec parcimonie.
Cas d'utilisation standard:

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

Mais cela semble forcer le design suivant:

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

Pour que cela redevienne une exception vérifiée.

D'accord, mais allons-y avec ça. Si vous donnez une mauvaise entrée, vous obtenez une erreur d'exécution. Tout d’abord, c’est en fait une politique assez difficile à mettre en œuvre uniformément, car vous pourriez devoir effectuer la conversion très opposée:

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

Et pire - lors de la vérification de 0 <= pct && pct <= 100, le code client devrait pouvoir fonctionner de manière statique. Ce n'est pas le cas pour des données plus avancées telles qu'une adresse e-mail, ou pire, quelque chose qui doit être vérifié par rapport à une base de données. Par conséquent, le code client ne peut -valider.

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

85
djechlin

Traitez IllegalArgumentException comme une vérification preconditions et tenez compte du principe de conception: Une méthode publique doit à la fois connaître et documenter publiquement ses propres conditions préalables.

Je conviens que cet exemple est correct:

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

Si EmailUtil est opaque , cela signifie qu'il y a une raison pour laquelle les conditions préalables ne peuvent pas être décrites à l'utilisateur final, alors 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 s'agit peut-être d'une méthode privée appartenant à la classe en question, IllegalArgumentException est correct si et seulement si ses conditions préalables peuvent être décrites dans la documentation de la fonction. C'est une version correcte aussi: 

/** @param String email An email with an address in the form [email protected]
 * 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");
  }
}

Cette conception pourrait aller dans les deux sens.

  • Si les conditions préalables sont coûteuses à décrire ou si le cours est destiné à être utilisé par des clients qui ne savent pas si leurs courriels sont valides, utilisez ParseException. La méthode de niveau supérieur ici est nommée scanEmail, ce qui indique que l'utilisateur final a l'intention d'envoyer un courrier électronique non étudié, ce qui est probablement correct.
  • Si les conditions préalables peuvent être décrites dans la documentation de la fonction et que la classe n'a pas l'intention d'entrer invalide et qu'une erreur du programmeur est indiquée, utilisez IllegalArgumentException. Bien que "non coché", le "contrôle" se déplace vers la Javadoc qui documente la fonction, ce que le client est censé respecter.IllegalArgumentException où le client ne peut pas dire que son argument est illégal avant, c'est faux.

Une note sur IllegalStateException : Cela signifie que "l'état interne de cet objet (variables d'instance privée) ne peut pas exécuter cette action". L'utilisateur final ne peut pas voir l'état privé. Par conséquent, il a la priorité sur 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 quand c'est préférable aux exceptions vérifiées, bien que des choses comme initialiser deux fois ou perdre une connexion à une base de données qui n'est pas rétablie soient des exemples.

0
djechlin

La documentation api pour IllegalArgumentException est:

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

En regardant comment il est utilisé dans les bibliothèques jdk , je dirais:

  • Cela semble être une mesure défensive de se plaindre de données manifestement erronées avant que celles-ci ne soient prises en compte et ne provoquent l’échec de quelque chose à mi-parcours avec un message d’erreur insensé.

  • Il est utilisé dans les cas où il serait trop ennuyeux de lancer une exception vérifiée (bien que cela apparaisse dans le code Java.lang.reflect, où l'inquiétude concernant les niveaux ridicules de levée d'exception vérifiée n'est pas évidente).

J'utiliserais IllegalArgumentException pour effectuer une vérification d'argile défensive de dernier recours pour les utilitaires communs (essayer de rester cohérent avec l'utilisation de jdk), dans laquelle on s'attend à ce qu'un mauvais argument soit une erreur de programmeur, similaire à un NPE. Je ne l'utiliserais pas pour implémenter la validation dans le code d'entreprise. Je ne l'utiliserais certainement pas pour l'exemple de courrier électronique.

Lorsque vous parlez de "mauvaise entrée", vous devez prendre en compte l’origine de l’entrée.

Si l'entrée est entrée par un utilisateur ou par un autre système externe que vous ne contrôlez pas, vous devez vous attendre à ce qu'elle soit invalide et toujours la valider. C'est parfaitement correct 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 la saisie provient de votre propre système, par exemple, Dans votre base de données, ou dans d’autres parties de votre application, vous devriez pouvoir vous fier à sa validité (celle-ci aurait dû être validée avant d’être disponible). Dans ce cas, il est parfaitement correct de lancer une exception non contrôlée, telle qu'une exception IllegalArgumentException, qui ne doit pas être interceptée (en général, vous ne devez jamais intercepter d'exceptions non contrôlées). C'est une erreur du programmeur que la valeur invalide soit arrivée là en premier lieu;) Vous devez la réparer.

19
Tom

Lancer les exceptions d'exécution "avec parcimonie" n'est pas vraiment une bonne politique - Java efficace 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, vous devriez alors émettre une exception non contrôlée; vous voulez que le programmeur ait une trace de la pile de l'endroit où le problème de logique s'est produit, ne pas essayer de le gérer vous-même.)

S'il n'y a pas d'espoir de récupération, n'hésitez pas à utiliser des exceptions non contrôlées; il ne sert à rien de les attraper, alors c'est parfaitement correct.

Votre exemple ne permet pas de savoir exactement dans quel cas cet exemple figure dans votre code.

11
Louis Wasserman

Toute API doit vérifier la validité de chaque paramètre de toute méthode publique avant de l'exécuter:

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

Ils représentent 99,9% des erreurs de temps dans l’application car il s’agit d’opérations impossibles, c’est donc un bogue qui devrait bloquer l’application (il s’agit donc d’une erreur irrécupérable).

Dans ce cas et en suivant l’approche d’échec rapide, vous devez laisser l’application se terminer pour éviter de corrompre son état.

5

Comme spécifié dans le tutoriel officiel 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 à partir de l'exception, faites-en une exception non contrôlée.

Si j'ai une application qui interagit avec la base de données en utilisant JDBC, j'ai une méthode qui prend l'argument sous la forme int item et double price. price pour l'élément correspondant est lu à partir de la table de base de données. Je multiplie simplement le nombre total de item acheté par la valeur price et renvoie le résultat. Bien que je sois toujours sûr (à la fin de l’application) que la valeur du champ prix dans le tableau ne pourra jamais être négative. Mais que faire si la valeur du prix sort négatif? Cela montre qu'il existe un problème sérieux avec 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 ni anticiper ni récupérer. C'est un BUG dans votre base de données. Donc, et IllegalArguementException() devrait être levé dans ce cas, ce qui indiquerait que the price can't be negative
J'espère avoir clairement exprimé mon point de vue.

4
Vishal K