Regresando de un bloque finalmente en Java

Recientemente me sorprendí al descubrir que es posible tener una statement de devolución en un bloque finally en Java.

Parece que mucha gente piensa que es malo hacer lo que se describe en ‘ No devolver en una cláusula final ‘. Rascando un poco más profundo, también encontré ‘ El retorno de Java no siempre ‘, que muestra algunos ejemplos bastante horribles de otros tipos de control de flujo en los bloques finalmente.

Entonces, mi pregunta es, ¿alguien puede darme un ejemplo en el que una sentencia de devolución (u otro control de flujo) en un bloque final produzca un código mejor / más legible?

Los ejemplos que proporcionó son motivo suficiente para no usar el control de flujo desde el final.

Incluso si hay un ejemplo inventado en el que es “mejor”, considere al desarrollador que debe mantener su código más tarde y que podría no estar al tanto de las sutilezas. Ese pobre desarrollador podría ser tú …

Realmente tuve un tiempo difícil para localizar un error hace años que fue causado por esto. El código era algo así como:

Object problemMethod() { Object rtn = null; try { rtn = somethingThatThrewAnException(); } finally { doSomeCleanup(); return rtn; } } 

Lo que sucedió es que la excepción fue lanzada en algún otro código. Se estaba capturando y registrando y volviendo a crear dentro del método somethingThatThrewAnException() . Pero la excepción no se estaba propagando hasta el pasado problemMethod() . Después de MUCHO tiempo de mirar esto, finalmente lo rastreamos hasta el método de retorno. El método de retorno en el bloque finally fue básicamente detener la excepción que sucedió en el bloque try de propagarse aunque no fue capturado.

Como han dicho otros, aunque es legal regresar de un locking final según las especificaciones de Java, es algo MALO y no debería hacerse.

javac advertirá de devolución finalmente si usas -Xlint: finalmente. Originalmente, javac no emitió advertencias: si algo está mal con el código, no debería comstackrse. Lamentablemente, la compatibilidad con versiones anteriores significa que no se pueden prohibir tonterías ingeniosas imprevistas.

Se pueden lanzar excepciones desde los bloques finally, pero en ese caso el comportamiento exhibido es casi seguramente lo que quieres.

Agregar estructuras de control y devoluciones a {} bloques finalmente es solo otro ejemplo de abusos “solo porque puedes” que están dispersos en prácticamente todos los lenguajes de desarrollo. Jason tenía razón al sugerir que podría convertirse fácilmente en una pesadilla de mantenimiento; los argumentos en contra de los beneficios iniciales de las funciones se aplican más, por lo que a este caso de “devoluciones tardías”.

Finalmente, existen bloques para un propósito, para permitirle ordenar completamente después de usted, sin importar lo que sucedió en el código anterior. Principalmente, esto es cerrar / liberar punteros de archivo, conexiones de bases de datos, etc., aunque podría ver que se estira para agregar una auditoría a medida.

Cualquier cosa que afecte el retorno de la función debería estar en el bloque try {}. Incluso si tuvieras un método mediante el cual verificaras un estado externo, hicieras una operación que requiriera mucho tiempo, luego verificaras ese estado nuevamente en caso de que se volviera inválido, igual querrías el segundo control dentro del bash {} – si finalmente se alojó adentro {} y la larga operación falló, entonces estarías revisando ese estado una segunda vez innecesariamente.

Una simple prueba Groovy:

 public class Instance { List runningThreads = new ArrayList() void test(boolean returnInFinally) { println "\ntest(returnInFinally: $returnInFinally)" println "--------------------------------------------------------------------------" println "before execute" String result = execute(returnInFinally, false) println "after execute -> result: " + result println "--------------------------------------------------------------------------" println "before execute" try { result = execute(returnInFinally, true) println "after execute -> result: " + result } catch (Exception ex) { println "execute threw exception: " + ex.getMessage() } println "--------------------------------------------------------------------------\n" } String execute(boolean returnInFinally, boolean throwError) { String thread = Thread.currentThread().getName() println "...execute(returnInFinally: $returnInFinally, throwError: $throwError) - thread: $thread" runningThreads.add(thread) try { if (throwError) { println "...error in execute, throw exception" throw new Exception("as you liked :-)") } println "...return 'OK' from execute" return "OK" } finally { println "...pass finally block" if (returnInFinally) return "return value from FINALLY ^^" // runningThreads.remove(thread) } } } Instance instance = new Instance() instance.test(false) instance.test(true) 

Salida:

 test(returnInFinally: false) ----------------------------------------------------------------------------- before execute ...execute(returnInFinally: false, throwError: false) - thread: Thread-116 ...return 'OK' from execute ...pass finally block after execute -> result: OK ----------------------------------------------------------------------------- before execute ...execute(returnInFinally: false, throwError: true) - thread: Thread-116 ...error in execute, throw exception ...pass finally block execute threw exception: as you liked :-) ----------------------------------------------------------------------------- test(returnInFinally: true) ----------------------------------------------------------------------------- before execute ...execute(returnInFinally: true, throwError: false) - thread: Thread-116 ...return 'OK' from execute ...pass finally block after execute -> result: return value from FINALLY ^^ ----------------------------------------------------------------------------- before execute ...execute(returnInFinally: true, throwError: true) - thread: Thread-116 ...error in execute, throw exception ...pass finally block after execute -> result: return value from FINALLY ^^ ----------------------------------------------------------------------------- 

Pregunta:

Un punto interesante para mí fue ver cómo Groovy trata con los retornos implícitos. En Groovy es posible “regresar” desde un método simplemente dejando un valor al final (sin devolución). ¿Qué crees que sucede, si elimina el comentario de la línea runningThreads.remove (..) en la statement finally – esto sobrescribirá el valor de retorno regular (“OK”) y cubrirá la excepción?