web-dev-qa-db-fra.com

Quelle est l'utilisation prévue de IllegalStateException?

Cela a été abordé lors d'une discussion avec un collègue aujourd'hui.

Les Javadocs pour Java/ IllegalStateException déclarent qu'il:

Signale qu'une méthode a été invoquée à un moment illégal ou inapproprié. En d'autres termes, l'environnement Java ou l'application Java n'est pas dans un état approprié pour l'opération demandée.

Et Effective Java dit (Point 60, page 248):

IllegalStateException est une autre exception fréquemment réutilisée. Il s’agit généralement d’une exception à lancer si l’invocation est illégale en raison de l’état de l’objet récepteur. Par exemple, ce serait l'exception à lancer si l'appelant tentait d'utiliser un objet avant qu'il ne soit correctement initialisé.

Il semble y avoir un peu de discordance ici. La deuxième phrase des javadocs donne l’impression que l’exception pourrait décrire une condition très large concernant l’état d’exécution Java, mais la description dans Effective Java donne l’impression qu’elle est utilisée pour des conditions liées spécifiquement à l’état de l’objet dont méthode a été appelée.

Les utilisations que j'ai vues dans le JDK (par exemple, collections, Matcher) et dans Guava semblent définitivement appartenir à la catégorie évoquée par Effective Java ("Cet objet est dans un état où cette méthode ne peut pas être appelée"). Cela semble également cohérent avec le frère de IllegalStateException, IllegalArgumentException.

Existe-t-il des utilisations IllegalStateException légitimes dans le JDK ayant un rapport avec "l'environnement Java" ou "l'application Java"? Ou bien des guides de bonnes pratiques préconisent-ils de l'utiliser pour un état d'exécution plus large? Sinon, pourquoi les javadocs sont-ils formulés comme ça? ;)

69
Andrew McNamee

Voici une utilisation particulièrement légitime de cette exception dans JDK (voir: URLConnection.setIfModifiedSince(long) parmi plus de 300 autres utilisations de celle-ci:

public void setIfModifiedSince(long ifmodifiedsince) {
    if (connected)
        throw new IllegalStateException("Already connected");
    ifModifiedSince = ifmodifiedsince;
}

Je pense que l'exemple est assez clair. Si l'objet est dans un état particulier ("Déjà connecté"), certaines opérations ne doivent pas être appelées. Dans ce cas, lorsque la connexion a été établie, certaines propriétés ne peuvent pas être définies.

Cette exception est particulièrement utile lorsque votre classe a un état (machine à états?) Qui change avec le temps, rendant certaines méthodes inutiles ou impossibles. Pensez à une classe Car qui possède les méthodes start(), stop() et fuel(). Lorsque vous appelez start() deux fois, les uns après les autres, il n’ya probablement aucun problème, mais ravitailler une voiture démarrée est certainement une mauvaise idée. À savoir - la voiture est dans un mauvais état.

On peut soutenir qu'une bonne API ne devrait pas nous permettre d'appeler des méthodes dans un état incorrect afin que des problèmes comme celui-ci soient découverts au moment de la compilation, pas au moment de l'exécution. Dans cet exemple particulier, la connexion à une URL doit renvoyer un objet différent avec un sous-ensemble de méthodes, qui sont toutes valides après la connexion.

38

Voici un exemple dans le JDK. Il existe une classe privée de package appelée Java.lang.Shutdown. Si le système s'arrête et que vous essayez d'ajouter un nouveau hook, il lève l'exception IllegalStateException. On pourrait affirmer que cela répond aux critères du guide "javadoc" - car c'est l'environnement Java qui est dans le mauvais état.

class Shutdown {    
...

   /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
    * but does not do any security checks.
    */
    static void add(int slot, Runnable hook) {
        synchronized (lock) {
            if (state > RUNNING)
                throw new IllegalStateException("Shutdown in progress");

            if (hooks[slot] != null)
                throw new InternalError("Shutdown hook at slot " + slot + " already registered");

            hooks[slot] = hook;
        }
    }

Toutefois, cela montre également qu’il n’existe vraiment aucune distinction entre les directives "javadoc" et "Java effectif". En raison de la manière dont Shutdown est implémenté, l’arrêt de la JVM est stocké dans un champ appelé state. Par conséquent, il respecte également les consignes "Effective Java" pour l'utilisation de IllegalStateException, car le champ "state" fait partie de l'état de l'objet récepteur. Étant donné que l'état de l'objet récepteur (Shutdown) est incorrect, il lève l'exception IllegalStateException.

À mon avis, les deux descriptions du moment où il faut utiliser IllegalStateException sont cohérentes. La description Effective Java est un peu plus pratique, c'est tout. Pour la plupart d'entre nous, la partie la plus importante de l'ensemble de l'environnement Java est la classe que nous écrivons actuellement, c'est donc ce sur quoi l'auteur se concentre. 

4
Guido Simone

Je suppose que si vous voyez l'utilisation de IllegalStateException, je dirais seconde si elle est plus appropriée. Cette exception est utilisée dans beaucoup de paquets

  • Java.net
  • Java.nio
  • Java.util
  • Java.util.concurrrent etc

Pour spécifier un exemple ArrayBlockingQueue.add lève cette exception si la file d'attente est déjà pleine. Maintenant, full est l'état de l'objet et il est invoqué à un moment inapproprié ou illégal.

Je suppose que les deux signifient la même chose mais différence de formulation.

2
Amit Deshpande

Dans une bibliothèque donnée, il devrait lancer IllegalStateException ou IllegalArgumentException chaque fois qu'il détecte un bogue en raison du code utilisateur, alors que la bibliothèque devrait lancer AssertionError chaque fois qu'il détecte un bogue en raison de sa propre implémentation.

Par exemple, dans les tests de la bibliothèque, vous pouvez vous attendre à ce qu'elle lance une IllegalStateException lorsque l'ordre des appels de méthode est incorrect. Mais vous ne vous attendez jamais à ce que la bibliothèque jette une AssertionError.

0
Yang Bo

J'ai rencontré ceci avec:

try {
    MessageDigest digest = MessageDigest.getInstance("SHA-1");
    ...
} catch (NoSuchAlgorithmException e) {
    throw new AssertionError(e);
}

Je pense qu'il ne sera pas pratique pour moi de placer IllegalStateException ici à la place de AssertionException même si cela entre dans la catégorie "environnement Java".

0
antak

Il n'y a pas de «divergence» ici. Le libellé de Bloch n’exclut pas ce qui est écrit dans le JLS. Bloch dit simplement que si vous avez la circonstance A, lancez cette exception. Il est pas en train de dire que cette exception est/devrait être levée seulement dans cet état. Le JLS dit que cette exception est levée si A, B ou C. 

0
user207421