Me topé con un código que se veía así:
void run() {
try {
doSomething();
} catch (Exception ex) {
System.out.println("Error: " + ex);
throw ex;
}
}
void doSomething() {
throw new RuntimeException();
}
Este código me sorprende porque parece que el run()
método-es capaz de arrojar un Exception
, ya que lo atrapa Exception
y luego lo vuelve a arrojar, pero no se declara que el método arroje Exception
y aparentemente no es necesario. Este código se compila muy bien (en Java 11 al menos).
Mi expectativa sería que tendría que declarar throws Exception
en el run()
método-.
Información extra
De manera similar, si doSomething
se declara que se lanza, IOException
entonces solo se IOException
debe declarar en el run()
método-, aunque Exception
se atrape y se vuelva a lanzar.
void run() throws IOException {
try {
doSomething();
} catch (Exception ex) {
System.out.println("Error: " + ex);
throw ex;
}
}
void doSomething() throws IOException {
// ... whatever code you may want ...
}
Pregunta
A Java generalmente le gusta la claridad, ¿cuál es la razón detrás de este comportamiento? ¿Siempre ha sido así? ¿Qué en la especificación del lenguaje Java permite que el run()
método no necesite declarar throws Exception
en los fragmentos de código anteriores? (Si quisiera agregarlo, IntelliJ me advierte que Exception
nunca se tira).
javac
: he estado encontrando casos en los que el compilador Eclipse fue más indulgente.-source 1.6
bandera genera un error de compilación como se esperaba. Compilar con la compatibilidad fuente 7 no no elevar el error de compilaciónIn detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
Respuestas:
No he escaneado las
JLS
preguntas que ha hecho en su pregunta, así que tome esta respuesta con un grano de sal. Quería hacer un comentario, pero habría sido demasiado grande.A veces me parece gracioso, cómo
javac
es bastante "inteligente" en algunos casos (como en su caso), pero deja muchas otras cosas para ser manejadas más adelanteJIT
. En este caso, es solo que el compilador "puede decir" que soloRuntimeException
se capturaría un. Esto es obvio, es lo único que arrojasdoSomething
. Si cambia ligeramente su código a:verá un comportamiento diferente, porque ahora
javac
puede decir que hay una nuevaException
que está lanzando, no relacionada con la que atrapó.Pero las cosas están lejos de ser ideales, puede "engañar" al compilador una vez más a través de:
OMI, por
ex2 = ex;
eso no debería fallar nuevamente, pero lo hace.Por si acaso esto fue compilado con
javac 13+33
fuente
ex2
lanzará la excepción, se creó originalmente como unException
pero luego se le reasigna yex
, por lo tanto, el compilador no puede ser inteligente.JLS
podría venir y proporcionar las citas necesarias para probar esto; desafortunadamente no los tengo.ex = ex;
), la heurística ya no se aplica. Este comportamiento parece aplicarse a todos los niveles de origen del 7 al 11 y probablemente del 13