tipos - todas las excepciones en java
¿Por qué se marcan las excepciones controladas permitidas para el código que no arroja excepciones? (3)
Citando la Especificación del Lenguaje Java, §11.2.3 :
Es un error en tiempo de compilación si una cláusula de captura puede detectar la clase de excepción comprobada E1 y no es el caso que el bloque try correspondiente a la cláusula catch pueda arrojar una clase de excepción comprobada que sea una subclase o superclase de E1, a menos que E1 Excepción o una superclase de Excepción.
Supongo que esta regla se originó mucho antes que Java 7, donde las capturas múltiples no existían. Por lo tanto, si tuviera un bloque try
que arrojara una multitud de excepciones, la forma más fácil de capturar todo sería capturar una superclase común (en el peor de los casos, Exception
o Throwable
si también quiere detectar Error
s).
Tenga en cuenta que es posible que no capte un tipo de excepción que no esté relacionado con lo que se arroja realmente: en su ejemplo, capturar cualquier subclase de Throwable
que no sea una RuntimeException
será un error:
try {
System.out.println("hello");
} catch (IOException e) { // compilation error
e.printStackTrace();
}
Editar por OP: la parte principal de la respuesta es el hecho de que los ejemplos de preguntas funcionan solo para la clase Excepción. Generalmente no se permite capturar excepciones comprobadas en lugares aleatorios del código. Perdón si confundí a alguien usando estos ejemplos.
En Java, los métodos que lanzan excepciones controladas ( Exception o sus subtipos - IOException, InterruptedException, etc.) deben declarar throws statement:
public abstract int read() throws IOException;
Los métodos que no declaran arrojar declaración no pueden arrojar excepciones marcadas.
public int read() { // does not compile
throw new IOException();
}
// Error: unreported exception java.io.IOException; must be caught or declared to be thrown
Pero detectar excepciones comprobadas en métodos seguros sigue siendo legal en Java:
public void safeMethod() { System.out.println("I''m safe"); }
public void test() { // method guarantees not to throw checked exceptions
try {
safeMethod();
} catch (Exception e) { // catching checked exception java.lang.Exception
throw e; // so I can throw... a checked Exception?
}
}
En realidad no. Es un poco divertido: el compilador sabe que e no es una excepción comprobada y permite volver a lanzarlo. Las cosas son incluso un poco ridículas, este código no se compila:
public void test() { // guarantees not to throw checked exceptions
try {
safeMethod();
} catch (Exception e) {
throw (Exception) e; // seriously?
}
}
// Error: unreported exception java.lang.Exception; must be caught or declared to be thrown
El primer fragmento fue una motivación para una pregunta.
El compilador sabe que las excepciones comprobadas no pueden arrojarse dentro de un método seguro, por lo que tal vez debería permitir capturar solo las excepciones no verificadas.
Volviendo a la pregunta principal , ¿hay alguna razón para implementar la captura de excepciones marcadas de esta manera? ¿Es solo un defecto en el diseño o me faltan algunos factores importantes, tal vez incompatibilidades hacia atrás? ¿Qué podría salir mal si solo RuntimeException
fuera atrapada en este escenario? Los ejemplos son muy apreciados.
El problema aquí es que las limitaciones de las excepciones controladas / no comprobadas afectan a lo que su código puede arrojar , no a lo que está permitido atrapar . Si bien aún puede atrapar cualquier tipo de Exception
, las únicas que puede arrojar de nuevo son las que no se marcaron. (Esta es la razón por la que convertir su excepción no verificada en una excepción marcada rompe su código).
Capturar una excepción sin marcar con Exception
es válida, porque las excepciones sin marcar (también conocidas como RuntimeException
s) son una subclase de Exception, y sigue las reglas de polimorfismo estándar; no convierte la excepción capturada en una Exception
, del mismo modo que almacenar una String
en un Object
no convierte la String
en un Object
. Polimorfismo significa que una variable que puede contener un Object
puede contener cualquier cosa derivada de un Object
(como una String
). Del mismo modo, como Exception
es la superclase de todos los tipos de excepción, una variable de tipo Exception
puede contener cualquier clase derivada de Exception
, sin convertir el objeto en una Exception
. Considera esto:
import java.lang.*;
// ...
public String iReturnAString() { return "Consider this!"; }
// ...
Object o = iReturnAString();
A pesar de que el tipo de variable es Object
, o
aún almacena una String
, ¿no es así? Del mismo modo, en tu código:
try {
safeMethod();
} catch (Exception e) { // catching checked exception
throw e; // so I can throw... a checked Exception?
}
Lo que esto significa es en realidad "capturar cualquier cosa compatible con Exception
clase (es decir, Exception
y todo lo que se derive de ella)". Lógica similar se usa en otros idiomas, también; por ejemplo, en C ++, la captura de std::exception
también detectará std::runtime_error
, std::logic_error
, std::bad_alloc
, cualquier excepción creada por el usuario definida correctamente, etc., porque todas derivan de std::exception
tl; dr: no está detectando excepciones controladas , está atrapando excepciones. La excepción solo se convierte en una excepción comprobada si la convierte en un tipo de excepción comprobada.
Java 7 introdujo una comprobación de tipo de excepción más inclusiva .
Sin embargo, en Java SE 7, puede especificar los tipos de excepción FirstException y SecondException en la cláusula throws en la declaración del método rethrowException. El compilador de Java SE 7 puede determinar que la excepción lanzada por la instrucción throw e debe provenir del bloque try, y que las únicas excepciones lanzadas por el bloque try pueden ser FirstException y SecondException.
Este pasaje habla de un bloque try
que arroja específicamente FirstException
y SecondException
; aunque el bloque catch
arroja Exception
, el método solo necesita declarar que arroja FirstException
y SecondException
, no Exception
:
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }
Esto significa que el compilador puede detectar que los únicos tipos de excepción posibles lanzados en la test
son Error
s o RuntimeException
s, ninguno de los cuales debe capturarse. Cuando throw e;
, puede decir, incluso cuando el tipo estático es Exception
, que no necesita ser declarado o vuelto a capturar.
Pero cuando lo lanzas a Exception
, esto pasa por alto esa lógica. Ahora el compilador lo trata como una Exception
ordinaria que debe capturarse o declararse.
La razón principal para agregar esta lógica al compilador era permitirle al programador especificar solo subtipos específicos en la cláusula throws
al volver a lanzar una Exception
general que capture esos subtipos específicos. Sin embargo, en este caso, le permite capturar una Exception
general y no tener que declarar ninguna excepción en una cláusula throws
, porque no se marcan los tipos específicos que se pueden marcar como excepciones.