java - excepciones - MĂșltiples declaraciones de retorno sin error del compilador
try catch throw java (8)
Esta fue una pregunta de entrevista:
public class Demo {
public static void main(String[] args) {
System.out.println(foo());
}
static String foo() {
try {
return "try ...";
} catch (Exception e) {
return "catch ...";
} finally {
return "finally ..."; //got as result
}
}
}
Mi pregunta es por qué no hay errores de tiempo de compilación.
Cuando tengo la declaración return en mi bloque
finally
, es probable que regrese
finally
lugar de
try
y
catch
bloque.
Intenté compilar este código con la opción
-Xlint
, da una advertencia como.
warning: [finally] finally clause cannot complete normally
(Para una respuesta corta: lea las partes en negrita y cursiva de la respuesta)
El flujo de ejecución según los documentos de Java 8. Te proporciona los detalles. Puede inferir la ejecución de declaraciones de devolución en función de lo siguiente.
Una declaración de prueba con un bloque finalmente se ejecuta ejecutando primero el bloque de prueba.
Entonces hay una opción:
• Si la ejecución del bloque try se completa normalmente, entonces se ejecuta el bloque finalmente y luego hay una opción:
- Si el bloque finalmente se completa normalmente, entonces la instrucción try se completa normalmente.
- Si el bloque finalmente se completa abruptamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S.
• Si la ejecución del bloque try se completa abruptamente debido a un lanzamiento de un valor V, entonces hay una opción:
- Si el tipo de V en tiempo de ejecución es una asignación compatible con una clase de excepción capturable de cualquier cláusula catch de la declaración try, entonces se selecciona la primera cláusula catch (más a la izquierda). El valor V se asigna al parámetro de la cláusula catch seleccionada y se ejecuta el Bloque de esa cláusula catch.
Entonces hay una opción:
›Si el bloque catch se completa normalmente, se ejecuta el bloque finalmente. Entonces hay una opción:
»Si el bloque finalmente se completa normalmente, entonces la instrucción try se completa normalmente.
»Si el bloque finalmente se completa abruptamente por alguna razón, entonces la instrucción try se completa abruptamente por la misma razón.
› Si el bloque catch se completa abruptamente por la razón R, entonces se ejecuta el bloque finalmente. Entonces hay una opción:
»Si el bloque finalmente se completa normalmente, entonces la instrucción try se completa abruptamente por la razón R.
» Si el bloque finalmente se completa abruptamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S (y la razón R se descarta).
- Si el tipo de V en tiempo de ejecución no es compatible con una clase de excepción capturable de cualquier cláusula catch de la declaración try, entonces se ejecuta el bloque finalmente.
Entonces hay una opción:
›Si el bloque finalmente se completa normalmente, entonces la instrucción try se completa abruptamente debido a un lanzamiento del valor V.
›Si el bloque finalmente se completa abruptamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S (y el lanzamiento del valor V se descarta y se olvida).
• Si la ejecución del bloque try se completa abruptamente por cualquier otra razón R, entonces el bloque finalmente se ejecuta y luego hay una opción:
- Si el bloque finalmente se completa normalmente, entonces la instrucción try se completa abruptamente por la razón R.
- Si el bloque finalmente se completa abruptamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S (y la razón R se descarta).
la explicación es clara en este javaDoc
Es esencialmente lo mismo que esto:
public boolean someMethod(){
if(1 == 1){
return true;
}
return false;
}
No dará un error de compilación, aunque dará una advertencia. El compilador solo dará un error cuando exista la posibilidad de que no se ejecute ninguna declaración de devolución.
Esto se describe en la Especificación del lenguaje Java:
La finalización abrupta de una cláusula final puede interrumpir la transferencia de control iniciada por una declaración de
return
.
Si la ejecución del bloque
try
se completa normalmente, entonces se ejecuta el bloquefinally
y luego hay una opción:
- Si el bloque
finally
se completa normalmente, entonces la instruccióntry
completa normalmente.- Si el bloque
finally
se completa abruptamente por la razón S, entonces la instruccióntry
completa abruptamente por la razón S.Si la ejecución del bloque
try
se completa abruptamente por cualquier otra razón R, entonces el bloquefinally
se ejecuta y luego hay una opción:
- Si el bloque
finally
se completa normalmente, entonces la instruccióntry
completa abruptamente por la razón R.- Si el bloque
finally
se completa abruptamente por la razón S, entonces la instruccióntry
completa abruptamente por la razón S (y la razón R se descarta).
No da un error de compilación porque está permitido por la Especificación del lenguaje Java.
Sin embargo, da un mensaje de advertencia porque incluir una declaración de
return
en el bloque final suele ser una mala idea.
Lo que sucede en tu ejemplo es lo siguiente.
Se ejecuta la declaración de
return
en el bloque
try
.
Sin embargo, el bloque
finally
siempre debe ejecutarse para que se ejecute después de que finalice el bloque
catch
.
La declaración de
return
que ocurre allí sobrescribe el resultado de la declaración de
return
anterior, por lo que el método devuelve el segundo resultado.
Del mismo modo, un bloque
finally
no debería generar una excepción.
Es por eso que la advertencia dice que el bloque
finally
debería completarse normalmente, es decir, sin
return
o lanzando una excepción.
No hay ningún error de tiempo de compilación ya que solo 1 y exactamente 1 de la declaración de
return
realidad devolverá el control al código de llamada.
Como se explicó en @Hoopje, el
return
dentro de
try
o
catch
se ejecutará primero, su respectiva declaración de retorno también se ejecutará.
Pero justo antes de devolver el control al código de llamada, ejecutará el bloque
finally
.
Ahora, este
block
también
return
algo, por lo que este retorno anula al anterior.
Pregunta brillante ... Según mi conocimiento, la declaración de devolución del bloque try and catch se transfiere finalmente a si ha agregado finalmente el bloque a su código. Así es como funciona.
Entonces, en este caso, todas las líneas de código se están ejecutando y puede intentar la depuración. Los tres bloques que probé debajo del código.
public class Main {
public static void main(String[] args) {
System.out.println(foo());
}
static String foo() {
try {
throw new Exception();
} catch (Exception e) {
return "catch ...";
} finally {
return "finally ..."; //got as result
}
}
}
Puede obtener la idea desde el siguiente enlace. Retornos múltiples: ¿Cuál establece el valor de retorno final?
Su código funciona bien porque solo hay una declaración de retorno en try, catch y finalmente bloquea. Se producirá un error de compilación si intenta escribir dos declaraciones de retorno dentro de una de try, catch o finalmente bloquea diciendo que hay una declaración de retorno inalcanzable.
intenta ejecutar esto:
imprimirá: 1, 2, 3 y luego lanzará una división por cero Excepción
public class Demo {
public static void main(String[] args) {
System.out.println(foo());
}
public static String print(int a){
System.out.println(a);
return String.valueOf(a/0);
}
static String foo() {
try {
return print(1);
} catch (Exception e) {
return print(2);
} finally {
return print(3);
}
}
}