Capturar una excepción y volver a lanzarla, pero no es una excepción

10

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 Exceptiony luego lo vuelve a arrojar, pero no se declara que el método arroje Exceptiony 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 Exceptionen el run()método-.

Información extra

De manera similar, si doSomethingse declara que se lanza, IOExceptionentonces solo se IOExceptiondebe declarar en el run()método-, aunque Exceptionse 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 Exceptionen los fragmentos de código anteriores? (Si quisiera agregarlo, IntelliJ me advierte que Exceptionnunca se tira).

Simon Forsberg
fuente
3
Interesante. ¿Qué compilador estás usando? Si se trata del compilador IDE, compruebe con javac: he estado encontrando casos en los que el compilador Eclipse fue más indulgente.
M. Prokhorov
2
Puedo reproducir este comportamiento en openjdk-8. Notablemente, compilar con la -source 1.6bandera genera un error de compilación como se esperaba. Compilar con la compatibilidad fuente 7 no no elevar el error de compilación
Vogel612
1
Parece que el compilador es más inteligente desde Java 7 y realiza más comprobaciones sobre las excepciones reales que podrían generarse.
michalk
2
Esta pregunta no es un duplicado y la respuesta se puede encontrar en el enlace que proporcionéIn 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.
michalk
2
El duplicado marcado actualmente es definitivamente relevante, pero no proporciona una respuesta IMO suficientemente detallada. Hay un enlace al JLS en los comentarios a la respuesta allí, además de eso no hay información.
Simon Forsberg el

Respuestas:

0

No he escaneado las JLSpreguntas 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 javaces bastante "inteligente" en algunos casos (como en su caso), pero deja muchas otras cosas para ser manejadas más adelante JIT. En este caso, es solo que el compilador "puede decir" que solo RuntimeExceptionse capturaría un. Esto es obvio, es lo único que arrojas doSomething. Si cambia ligeramente su código a:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

verá un comportamiento diferente, porque ahora javacpuede decir que hay una nueva Exceptionque 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:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

OMI, por ex2 = ex;eso no debería fallar nuevamente, pero lo hace.

Por si acaso esto fue compilado con javac 13+33

Eugene
fuente
Leí en algún enlace que alguien proporcionó que si reasigna la excepción atrapada en el bloque de captura, entonces el compilador no puede ser inteligente. Supongo que algo similar se aplica en este caso. El compilador sabe que se ex2lanzará la excepción, se creó originalmente como un Exceptionpero luego se le reasigna y ex, por lo tanto, el compilador no puede ser inteligente.
Simon Forsberg
@SimonForsberg alguien a quien le apasione JLSpodría venir y proporcionar las citas necesarias para probar esto; desafortunadamente no los tengo.
Eugene
Para el registro, cuando cambio el bloque catch para contener una reasignación de la excepción atrapada a sí mismo ( 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
Vogel612
Eche un vistazo a esta pregunta, que también es un duplicado. Este y el dup del dup posible lo explican y también enlazan con JLS.
michalk