Consideremos la situación en la que tiene el código proporcionado de:
public void delete() throws IOException, SQLException { // Non-Compliant
/* ... */
}
El peligro aquí es que el código que escribes para llamar a delete()
se verá así:
try {
foo.delete()
} catch (Exception e) {
/* ... */
}
Esto también es malo. Y será atrapado con otra regla que marca la captura de la clase de excepción base.
La clave es no escribir el código que hace que quieras escribir un código incorrecto en otro lugar.
La regla con la que te encuentras es bastante común. Checkstyle lo tiene en sus reglas de diseño:
ThrowsCount
Restringe las declaraciones de lanzamientos a un recuento específico (1 de forma predeterminada).
Justificación: las excepciones forman parte de la interfaz de un método. Declarar un método para lanzar demasiadas excepciones con raíces diferentes hace que el manejo de excepciones sea oneroso y conduce a prácticas de programación deficientes, como escribir código como catch (Exception ex). Esta verificación obliga a los desarrolladores a colocar excepciones en una jerarquía de tal manera que, en el caso más simple, solo un tipo de excepción debe ser verificado por el llamante, pero cualquier subclase puede ser capturada específicamente si es necesario.
Esto describe con precisión el problema y cuál es el problema y por qué no debería hacerlo. Es un estándar bien aceptado que muchas herramientas de análisis estático identificarán y marcarán.
Y mientras usted puede hacerlo de acuerdo con el diseño del idioma, y puede haber ocasiones en que sea lo correcto, es algo que debe ver y inmediatamente vaya "um, ¿por qué estoy haciendo esto?" Puede ser aceptable para el código interno, donde todos son lo suficientemente disciplinados como para no cumplir nunca el catch (Exception e) {}
, pero la mayoría de las veces he visto a personas recortar esquinas, especialmente en situaciones internas.
No hagas que las personas que usan tu clase quieran escribir código incorrecto.
Debo señalar que la importancia de esto se reduce con Java SE 7 y versiones posteriores, ya que una sola instrucción catch puede detectar múltiples excepciones ( Captura de múltiples tipos de excepciones y nuevas excepciones con la verificación de tipos mejorada de Oracle).
Con Java 6 y antes, tendría un código que parecía:
public void delete() throws IOException, SQLException {
/* ... */
}
y
try {
foo.delete()
} catch (IOException ex) {
logger.log(ex);
throw ex;
} catch (SQLException ex) {
logger.log(ex);
throw ex;
}
o
try {
foo.delete()
} catch (Exception ex) {
logger.log(ex);
throw ex;
}
Ninguna de estas opciones con Java 6 es ideal. El primer enfoque viola DRY . Múltiples bloques que hacen lo mismo, una y otra vez, una vez por cada excepción. ¿Quieres registrar la excepción y volver a lanzarla? De acuerdo. Las mismas líneas de código para cada excepción.
La segunda opción es peor por varias razones. Primero, significa que estás atrapando todas las excepciones. El puntero nulo queda atrapado allí (y no debería). Además, estás volviendo a generar un Exception
, lo que significa que la firma del método sería deleteSomething() throws Exception
, lo que hace que sea un desastre la pila, ya que las personas que usan tu código ahora están forzadas a catch(Exception e)
.
Con Java 7, esto no es tan importante porque puedes hacerlo en su lugar:
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
Además, la comprobación de tipo si uno sí detecta los tipos de excepciones que se lanzan:
public void rethrowException(String exceptionName)
throws IOException, SQLException {
try {
foo.delete();
} catch (Exception e) {
throw e;
}
}
El verificador de tipos reconocerá que e
puede solo ser de los tipos IOException
o SQLException
. Todavía no estoy demasiado entusiasmado con el uso de este estilo, pero no está causando tan mal código como en Java 6 (donde te obligaría a tener la firma del método como la superclase que se extienden las excepciones). / p>
A pesar de todos estos cambios, muchas herramientas de análisis estático (Sonar, PMD, Checkstyle) siguen aplicando las guías de estilo de Java 6. No es una mala cosa. Tiendo a estar de acuerdo con una advertencia para que still se aplique, pero puede cambiar la prioridad de ellos a mayor o menor según la forma en que su equipo los priorice.
Si se deben marcar o desmarcar las excepciones ... es una cuestión de g r e a t debate que puede encontrar fácilmente Innumerables publicaciones de blog que abordan cada lado del argumento. Sin embargo, si está trabajando con excepciones marcadas, probablemente debería evitar lanzar varios tipos, al menos en Java 6.