how - ¿Se está lanzando activamente AssertionError en la buena práctica de Java?
how to use assert java (6)
Esta pregunta ya tiene una respuesta aquí:
Al analizar ''Effective Java - Second Edition'' de Joshua Bloch, me topé con el siguiente código en la página 152:
double apply(double x, double y) {
switch(this) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("Unknown op: " + this);
}
Ahora lo que me confunde es que AssertionError
se lanza de forma activa. ¿Es eso considerado una buena práctica? A mi entender, las aserciones se utilizan para no interferir con el código, de modo que cuando la programación en java se inicia sin las aserciones habilitadas y las aseveraciones no se ejecutan, el comportamiento no cambia. Me sentiría un poco confundido si obtuviera una AssertionException
cuando ejecutara un programa sin siquiera tener activadas las aserciones.
Aunque entiendo que el caso de ejemplo puede ocurrir con bastante frecuencia, analiza un par de opciones diferentes y, si no es una de ellas, debería lanzar una excepción.
Entonces, ¿es una buena práctica lanzar una AssertionException
aquí, o sería mejor lanzar una diferente? Si es así, ¿cuál encajaría mejor? Tal vez IllegalArgumentException
?
Edite para aclarar: mi pregunta no es si deberíamos lanzar un Error
aquí, pero si queremos lanzar una Exception
o un Error
, ¿cuál debería ser? ¿Y es una buena práctica lanzar activamente AssertionError
s? La documentación dice: Arrojado para indicar que una afirmación ha fallado , así que tengo la sensación de que no debemos lanzarla activamente. ¿Es eso correcto?
Segunda edición: Pregunta clara: ¿Es una buena práctica lanzar activamente un AssertionError
, o debería evitarse, aunque sea posible? (Mi conjetura leyendo la documentación es la última)
Creo que tanto AssertionError como IllegalAE no son muy buenos aquí. El error de aserción no es bueno como se indica en la respuesta de Matt. Y los argumentos no están equivocados aquí, solo se pasan a un método que está equivocado en this
operación. Así que el IAE puede no ser bueno también. Por supuesto, esta es una pregunta y respuesta basada en la opinión también.
Además, no estoy seguro de que habilitar la aserción sea obligatorio para lanzar AssertionError o una aserciónError significa que las aserciones estaban habilitadas.
En mi opinión, un AssertionError
sería incorrecto de usar aquí.
De los documentos , un AssertionError extiende la clase base Error
Un error es una subclase de Throwable que indica problemas graves que una aplicación razonable no debería intentar detectar.
Un error debería ser fatal, mientras que espero que su programa maneje esto y muestre al usuario un mensaje de advertencia sobre la operación desconocida.
Si hay algo aquí, esperaría que se lanzara una UnsupportedOperationException
de UnsupportedOperationException
y se manejara en otra parte de la pila de llamadas.
Se lanza para indicar que la operación solicitada no es compatible.
Considere el caso donde, no en una calculadora, sino en cualquier flujo de código que use ENUMs:
Si un desarrollador agregara un nuevo valor a una enumeración existente, no esperaría que las funciones que hacen uso de esta enumeración existente invocen un error, solo porque el nuevo valor no es compatible.
Estoy de acuerdo con el Sr. Bloch aquí: las alternativas ( IllegalArgumentException
, IllegalStateException
y UnsupportedOperationException
) no transmiten correctamente la gravedad del problema, y las personas que llaman pueden tratar de detectar y manejar este caso por error. De hecho, si alguna vez se llega a esta línea, el programa en cuestión se rompe , y lo único que puede hacer es salir.
El punto aquí es que la enumeración tiene un conjunto finito de valores, por lo que debería ser imposible llegar a la línea de throw
; solo se produciría si la definición de la enumeración ha cambiado sin corregir también este método de instancia. Lanzar una RuntimeException
sugiere que la persona que llama cometió un error, cuando en realidad el método (y la enumeración) en sí está roto. El aumento explícito de un AssertionError
indica que los invariantes que este método espera se han violado.
La guayaba tiene un artículo útil que analiza cuándo plantear diferentes tipos de excepciones . Escriben:
Una aserción convencional es una verificación que solo debería fallar si la clase en sí (que contiene la verificación) se rompe de alguna manera. (En algunos casos, esto puede extenderse al paquete.) Pueden tomar varias formas, incluidas las condiciones posteriores, las condiciones invariantes de clase y las condiciones previas internas (en métodos no públicos).
Una verificación de condición imposible es aquella que no puede fallar a menos que el código circundante se modifique posteriormente, o que se violen gravemente nuestras suposiciones más profundas sobre el comportamiento de la plataforma. Estos deben ser innecesarios, pero a menudo son forzados porque el compilador no puede reconocer que una declaración es inaccesible, o porque sabemos algo sobre el flujo de control que el compilador no puede deducir.
La página dice que AssertionError
es la forma recomendada de manejar estos casos. Los comentarios en su clase de Verify
también ofrecen algunas ideas útiles sobre cómo elegir excepciones. En los casos en que AssertionError
parezca demasiado fuerte, el aumento de una VerifyException
puede ser un buen compromiso
En cuanto a la pregunta específica de Error
o RuntimeException
, realmente no importa (ambos no están verificados y, por lo tanto, potencialmente viajarán por la pila de llamadas sin ser detectados), pero es más probable que las personas que llaman intenten recuperarse de una RuntimeException
. El bloqueo de la aplicación en un caso como este es una característica , porque de lo contrario continuamos ejecutando una aplicación que es (en este punto) demostrablemente incorrecta. Ciertamente, es menos probable que las personas que llaman capten y manejen AssertionError
(o Error
o Throwable
), pero, por supuesto, las personas que llaman pueden hacer lo que quieran.
Respecto a los errores, el Tutorial de Java establece:
El segundo tipo de excepción es el error. Estas son condiciones excepcionales que son externas a la aplicación y de las cuales la aplicación generalmente no puede anticipar o recuperar.
Además, la guía de programación con aseveraciones establece:
No use aserciones para la verificación de argumentos en métodos públicos.
Así que creo que la excepción es la forma correcta de verificar este tipo de casos.
Recomiendo usar una new UnsupportedOperationException("Operator " + name() + " is not supported.");
ya que describe mejor el problema en mi opinión (es decir, un desarrollador agregó un valor de enumeración pero se olvidó de implementar el caso requerido).
Sin embargo, creo que este caso de muestra debería usar un patrón de diseño AbstractEnum
lugar de un interruptor:
PLUS {
double apply(double x, double y) {
return x + y;
}
},
MINUS {
double apply(double x, double y) {
return x - y;
}
},
TIMES {
double apply(double x, double y) {
return x * y;
}
},
DIVIDE {
double apply(double x, double y) {
return x / y;
}
};
abstract double apply(double x, double y);
Es menos propenso a errores ya que este código no se compilará hasta que se apply
todos los implementos de caso.
Según tengo entendido, su método es un método de enumeración de objetos. En la mayoría de los casos, cuando alguien agrega un nuevo valor de enumeración, también debe modificar el método de "aplicar". Debes lanzar la excepción UnsupportedOperationException en este caso.
Yo preferiría
double apply(double x, double y) {
switch(this) {
case PLUS: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
default: assert this==DIVIDE: return x / y;
}
}
- No deberíamos lanzar
AssertionError
porque debería estar reservado para aserciones reales. - Aparte de las afirmaciones y algunos bloques de captura, no debe haber un solo bit de código que no pueda alcanzarse de manera práctica.
Pero preferiría más https://.com/a/41324246/348975