Di ritorno da un blocco finalmente in Java


177

Di recente sono stato sorpreso di scoprire che è possibile avere una dichiarazione di ritorno in un blocco infine in Java.

Sembra che molte persone pensino che sia una brutta cosa fare come descritto in " Non tornare in una clausola finalmente ". Grattando un po 'più a fondo, ho anche scoperto che " il ritorno di Java non sempre " che mostra alcuni esempi piuttosto orribili di altri tipi di controllo del flusso nei blocchi finalmente.

Quindi, la mia domanda è: qualcuno può darmi un esempio in cui un'istruzione return (o altro controllo di flusso) in un blocco finally produce codice migliore / più leggibile?

Risposte:


90

Gli esempi forniti sono un motivo sufficiente per non utilizzare finalmente il controllo del flusso.

Anche se c'è un esempio inventato in cui è "migliore", considera lo sviluppatore che deve mantenere il codice in un secondo momento e che potrebbe non essere a conoscenza delle sottigliezze. Quel povero sviluppatore potresti anche essere tu ...


5
Sicuro. Immagino che lo stia chiedendo nel caso qualcuno mi possa dare qualche esempio davvero convincente dal lato del bene.
Matt Sheppard,

@MattSheppard in daos, registrerò spesso l'uscita di una query in una prova-finalmente
Blake

148

Ho avuto un periodo davvero difficile da rintracciare un bug anni fa causato da questo. Il codice era qualcosa del tipo:

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

Quello che è successo è che l'eccezione è stata lanciata in qualche altro codice. Veniva catturato, registrato e riproposto nel somethingThatThrewAnException()metodo. Ma l'eccezione non veniva propagata nel passato problemMethod(). Dopo un LUNGO tempo di osservazione, abbiamo finalmente rintracciato il metodo di ritorno. Il metodo di ritorno nel blocco finally era sostanzialmente quello di impedire la propagazione dell'eccezione avvenuta nel blocco try anche se non è stata rilevata.

Come altri hanno già detto, mentre è legale tornare da un blocco infine secondo le specifiche Java, è una cosa MALE e non dovrebbe essere fatto.


Dove si dovrebbe mettere il ritorno allora?
parsecer

@parsecer direi subito dopo aver chiamato qualcosaThatThrewAnException () all'interno del blocco try
Tiago Sippert

@parsecer, ?? Fallo nel solito modo, dopo finalmente.
Pacerier

21

javac avviserà di tornare finalmente se si utilizza -Xlint: finalmente. Inizialmente javac non ha emesso alcun avviso: se qualcosa non va nel codice, non dovrebbe essere compilato. Purtroppo la retrocompatibilità significa che la follia geniale non prevista non può essere proibita.

Le eccezioni possono essere generate da blocchi finalmente, ma in quel caso il comportamento esibito è quasi certamente quello che vuoi.


13

L'aggiunta di strutture di controllo e il ritorno ai blocchi {} finalmente sono solo un altro esempio di abusi "solo perché è possibile" che sono sparsi praticamente in tutti i linguaggi di sviluppo. Jason aveva ragione nel suggerire che potrebbe facilmente diventare un incubo per la manutenzione - gli argomenti contro i primi ritorni dalle funzioni si applicano di più, quindi in questo caso di "ritorni tardivi".

Finalmente esistono blocchi per uno scopo, per permetterti di riordinare completamente te stesso, qualunque cosa sia successa in tutto il codice precedente. Principalmente questo sta chiudendo / rilasciando puntatori di file, connessioni di database ecc., Anche se ho potuto vederlo allungato per dire aggiungendo nel controllo su misura.

Tutto ciò che influisce sul ritorno della funzione dovrebbe trovarsi nel blocco try {}. Anche se avessi un metodo in base al quale hai controllato uno stato esterno, fatto un'operazione che richiede tempo, quindi lo hai ricontrollato nel caso in cui non fosse più valido, vorrai comunque il secondo controllo all'interno del tentativo {} - se alla fine si trovava all'interno {} e l'operazione lunga non è riuscita, quindi verifichi inutilmente quello stato una seconda volta.


6

Un semplice test Groovy:

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)

Produzione:

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 ^^
-----------------------------------------------------------------------------

Domanda:

Un punto interessante per me è stato vedere come Groovy gestisce i rendimenti impliciti. In Groovy è possibile "ritornare" da un metodo semplicemente lasciando un valore alla fine (senza ritorno). Cosa pensi che accada se rimuovi il commento dalla riga runningThreads.remove (..) nell'istruzione finally - questo sovrascriverà il normale valore di ritorno ("OK") e coprirà l'eccezione ?!


0

Il ritorno dall'interno di un finallyblocco potrebbe exceptionsessere perso.

Un'istruzione return all'interno di un blocco finally causerà l'eliminazione di qualsiasi eccezione che potrebbe essere generata nel blocco try or catch.

Secondo le specifiche del linguaggio Java:

Se l'esecuzione del blocco try viene completata bruscamente per qualsiasi altro motivo R, viene eseguito il blocco finally e quindi è possibile scegliere:

   If the finally block completes normally, then the try statement
   completes  abruptly for reason R.

   If the finally block completes abruptly for reason S, then the try
   statement  completes abruptly for reason S (and reason R is
   discarded).

Nota: come da JLS 14.17 - una dichiarazione di ritorno viene sempre completata bruscamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.