¿Cuál es el uso previsto de IllegalStateException?

76

Esto surgió hoy en una discusión con un colega.

Los Javadocs para Java IllegalStateExceptionafirman que:

Señala que se ha invocado un método en un momento ilegal o inapropiado. En otras palabras, el entorno Java o la aplicación Java no se encuentran en un estado apropiado para la operación solicitada.

Y Effective Java dice (artículo 60, página 248):

Otra excepción comúnmente reutilizada es IllegalStateException. Esta es generalmente la excepción a lanzar si la invocación es ilegal debido al estado del objeto receptor. Por ejemplo, esta sería la excepción a lanzar si la persona que llama intenta usar algún objeto antes de que se haya inicializado correctamente.

Parece que hay un poco de discrepancia aquí. La segunda oración de los javadocs hace que parezca que la excepción podría describir una condición muy amplia sobre el estado de ejecución de Java, pero la descripción en Java efectivo hace que parezca que se usa para condiciones relacionadas específicamente con el estado del estado del objeto cuyo se ha llamado al método.

Los usos que he visto en el JDK (por ejemplo, colecciones Matcher) y en Guava definitivamente parecen caer en la categoría de la que habla Effective Java ("Este objeto está en un estado en el que no se puede llamar a este método"). Esto también parece consistente conIllegalStateException el hermano de ' IllegalArgumentException.

¿Hay IllegalStateExceptionusos legítimos en el JDK que se relacionen con el "entorno Java" o la "aplicación Java"? ¿O alguna guía de mejores prácticas aboga por su uso para el estado de ejecución más amplio? Si no es así, ¿por qué diablos están los javadocs redactados así? ;)

Andrew McNamee
fuente
4
En una nota tal vez no relacionada, noté que la etiqueta de StackOverflow [excepción de estado ilegal] dice In Java, an exception that occurs when using multiple threads, whereby one thread modifies an object in a way that makes it incompatible with the use of that object in the second thread, thus putting the object into an illegal state.. ¿Eh? ¿De donde viene esto?
Andrew McNamee
3
La "aplicación Java" es la que usted escribe y puede usar IllegalStateExceptionallí (ya sea directamente o porque está usando Guava, por ejemplo). ¿Dónde está la discrepancia?
Frank Pavageau
4
La etiqueta wiki parece falsa, he enviado una edición tomando prestado generosamente de esta pregunta; debería ver la nueva versión una vez que haya pasado la revisión por pares.
meriton

Respuestas:

43

Aquí hay un uso particularmente legítimo de esta excepción en JDK (ver: URLConnection.setIfModifiedSince(long)entre más de 300 usos de la misma:

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

Creo que el ejemplo es bastante claro. Si el objeto está en un estado particular (" Ya conectado "), algunas operaciones no deben llamarse. En este caso, cuando se estableció la conexión, no se pueden establecer algunas propiedades.

Esta excepción es especialmente útil cuando su clase tiene algún estado (¿máquina de estado?) Que cambia con el tiempo, haciendo que algunos métodos sean irrelevantes o imposibles. Pensar en una Carclase que tiene start(), stop()y fuel()métodos. Si bien llamar start()dos veces, una tras otra, probablemente no sea nada malo, pero cargar combustible en un automóvil encendido es ciertamente una mala idea. Es decir, el coche está en mal estado.

Podría decirse que una buena API no debería permitirnos llamar a métodos en un estado incorrecto para que problemas como ese se descubran en tiempo de compilación, no en tiempo de ejecución. En este ejemplo en particular, la conexión a una URL debería devolver un objeto diferente con un subconjunto de métodos, todos los cuales son válidos después de la conexión.

Tomasz Nurkiewicz
fuente
1
Estoy de acuerdo con tu respuesta. Pero esto no explica por qué Effectiva Java y Guava usan esta excepción como unIllegalArgumentException
Zaratustra
4

Aquí hay un ejemplo en el JDK. Hay un paquete de clase privada llamado java.lang.Shutdown. Si el sistema se está apagando e intenta agregar un nuevo gancho, lanza la IllegalStateException. Se podría argumentar que esto cumple con los criterios de la guía "javadoc", ya que es el entorno Java el que está en el estado incorrecto.

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;
        }
    }

Sin embargo, también ilustra que realmente no hay distinción entre la guía "javadoc" y la guía "Java efectivo". Debido a la forma en que se implementa el cierre, el cierre de la JVM se almacena en un campo llamado estado. Por lo tanto, también cumple con las pautas de "Java efectivo" sobre cuándo usar IllegalStateException, ya que el campo "estado" es parte del estado del objeto receptor. Dado que el objeto receptor (Shutdown) está en un estado incorrecto, lanza la IllegalStateException.

En mi opinión, las dos descripciones de cuándo usar IllegalStateException son consistentes. La descripción de Effective Java es un poco más práctica, eso es todo. Para la mayoría de nosotros, la parte más importante de todo el entorno Java es la clase que estamos escribiendo en este momento, por lo que el autor se centra en eso.

Guido Simone
fuente
1
¡Ah, me gusta este ejemplo! El InternalErrortiro justo debajo del ISE también demuestra lo útil que es hacer una distinción entre "Lo arruinaste" y "Yo lo arruiné". Además, muestra cómo el estado estático (podría decir "Aplicación Java" o estado "Entorno Java") todavía cuenta como estado en lo que respecta a los ISE.
Andrew McNamee
@AndrewMcNamee Creo que AssertionErrorsería más apropiado para los casos en los que sé que me equivoqué. Parece ser InternalErrorsolo porque está dentro del código JDK en lugar del código de la aplicación, una distinción dudosa.
Ramon
Sí, definitivamente estoy de acuerdo AssertionError; parece transmitir "Me equivoqué" en contraposición a "Tú lo arruinaste" (lo que implica ISE). Lo uso mucho en el defaultcaso de switches. La OMI InternalErrortodavía tiene un propósito útil; la persona que lee el seguimiento de la pila sabe que puede asumir "Whoa, el JDK se equivocó", pero los autores del JDK probablemente podrían haberlo omitido.
Andrew McNamee
2

Supongo que si ve el uso de IllegalStateException, diría segundo si es más apropiado. Esta excepción se utiliza en muchos paquetes.

  • java.net
  • java.nio
  • java.util
  • java.util.concurrrent, etc.

Para especificar un ejemplo, ArrayBlockingQueue.add lanza esta excepción si la cola ya está llena. Ahora lleno es el estado del objeto y se está invocando en un momento inapropiado o ilegal.

Supongo que ambos significan lo mismo pero con diferencias de redacción.

Amit Deshpande
fuente
1

Dada una biblioteca, debería lanzar un IllegalStateExceptiono IllegalArgumentExceptionsiempre que detecte un error debido al código de usuario, mientras que la biblioteca debería lanzar un AssertionErrorcada vez que detecte un error debido a la propia implementación de la biblioteca.

Por ejemplo, en las pruebas de la biblioteca, puede esperar que la biblioteca arroje un IllegalStateExceptionerror cuando el orden de las llamadas al método sea incorrecto. Pero nunca esperará que la biblioteca arroje un AssertionError.

Yang Bo
fuente
0

No hay ninguna "discrepancia" aquí. No hay nada en la redacción de Bloch que excluya lo que dice en la JLS. Bloch simplemente está diciendo que si tienes la circunstancia A, lanza esta excepción. Él es no decir que esta excepción es / debe ser tirado solamente en esta condición. El JLS dice que esta excepción se lanza si A, B o C.

user207421
fuente
Supongo que podrías decir eso. Pero, por otro lado, si tuviera circunstancias de uso intencionadas que fueran diferentes de la circunstancia que dio, entonces podría no ser tan significativo ya que sería menos específico. En otras palabras, si el ISE estuviera destinado a ser utilizado en otras circunstancias distintas al caso actual "Usted me llamó a este método, pero no estoy en un estado en el que pueda hacer eso", podría no ser tan informativo. Pero es cierto que creo que me estoy divirtiendo aquí;)
Andrew McNamee
@AndrewMcNamee me he dicho eso. No sé qué significa el resto de su comentario, pero está tratando de fabricar una discrepancia donde no existe. Estás cometiendo una falacia lógica.
user207421
0

Me encontré con esto con:

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

Creo que no será práctico para mí IllegalStateExceptioncolocarlo aquí en lugar de AssertionExceptionaunque esto se encuentre en la categoría "el entorno Java".

antak
fuente