Pregunta

Recientemente me sorprendió descubrir que es posible tener una declaración de devolución en un bloque finalmente en Java.

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

Entonces, mi pregunta es: ¿alguien puede darme un ejemplo en el que una declaración de retorno (u otro control de flujo) en un bloque finalmente produzca un código mejor o más legible?

¿Fue útil?

Solución

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

Incluso si hay un ejemplo artificial en el que es "mejor", considere al desarrollador que tiene que mantener su código más adelante y que tal vez no esté al tanto de las sutilezas.Ese pobre desarrollador podrías incluso ser tú...

Otros consejos

Me costó REALMENTE localizar un error hace años causado por esto.El código era algo como:

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

Lo que sucedió es que la excepción se produjo en algún otro código.Estaba siendo capturado, talado y arrojado de nuevo dentro del somethingThatThrewAnException() método.Pero la excepción no se estaba propagando más allá problemMethod().Después de MUCHO tiempo de analizar esto, finalmente lo rastreamos hasta el método de devolución.El método de retorno en el bloque finalmente básicamente detenía la propagación de la excepción que ocurrió en el bloque try aunque no fue detectada.

Como han dicho otros, si bien es legal regresar de un bloque finalmente de acuerdo con la especificación de Java, es algo MALO y no debe hacerse.

javac advertirá sobre el retorno finalmente si usa -Xlint:finally.Originalmente javac no emitía advertencias: si algo andaba mal con el código, no debería poder compilarse.Desafortunadamente, la compatibilidad con versiones anteriores significa que no se pueden prohibir tonterías ingeniosas e imprevistas.

Se pueden lanzar excepciones desde bloques finalmente, pero en ese caso el comportamiento exhibido es casi con certeza el que usted desea.

Agregar estructuras de control y retornos a los bloques finalmente{} son solo otro ejemplo de abusos "sólo porque puedes" que se encuentran 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 retornos tempranos de las funciones se aplican más a este caso de "devoluciones tardías".

Finalmente, los bloques existen con un propósito: permitirle ordenar completamente lo que ensucia, sin importar lo que haya sucedido en todo el código anterior.Principalmente, esto es cerrar/liberar punteros de archivos, conexiones de bases de datos, etc., aunque pude ver que se extendía para decir agregar una auditoría personalizada.

Todo lo que afecte el retorno de la función debe estar en el bloque try{}.Incluso si tuviera un método mediante el cual verificara un estado externo, realizara una operación que consumiera mucho tiempo y luego verificara ese estado nuevamente en caso de que dejara de ser válido, aún querría la segunda verificación dentro del try{} - si estuviera dentro finalmente{} y la larga operación falla, entonces estarías comprobando ese estado una segunda vez innecesariamente.

Una prueba sencilla y divertida:

public class Instance {

  List<String> runningThreads = new ArrayList<String>()

  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)

Producción:

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 maneja los retornos implícitos.En Groovy es posible "retornar" desde un método simplemente dejando un valor al final (sin retorno).¿Qué crees que sucede si descomentas el ejecutandoThreads.remove(..) línea en la declaración finalmente: ¿esto sobrescribirá el valor de retorno normal ("OK") y cubrirá la excepción?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top