184 votes

Revenir d'un bloc enfin en Java

J'ai été surpris récemment à trouver que c'est possible d'avoir une instruction de retour dans un bloc finally en Java.

Il semble que beaucoup de gens pensent que c'est une mauvaise chose à faire, comme décrit dans 'Ne renvoie pas à une clause finally'. De gratter un peu plus, j'ai aussi trouvé"de Java retour n'est pas toujours"qui montre quelques-unes assez horrible des exemples d'autres types de contrôle de flux dans enfin blocs.

Donc, ma question est, pouvez-vous me donner un exemple où une instruction de retour (ou d'un autre contrôle de flux) dans un bloc finally produit mieux / plus lisible le code?

160voto

John Meagher Points 6734

J'ai eu un moment difficile de repérer un bug il y a des années, qui a été causée par ce. Le code a été quelque chose comme:

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

Ce qui s'est passé, c'est que l'exception a été levée dans d'autres codes. Il fut pris et enregistré et relancés dans la somethingThatThrewAnException méthode. Mais l'exception n'était pas propagé passé problemMethod. Après un LONG temps de regarder ce nous avons finalement fait le suivi vers le bas pour le retour de la méthode. Le retour de la méthode dans le bloc finally est essentiellement l'arrêt de l'exception qui s'est passé dans le bloc try de se propager, même s'il n'a pas pris.

Comme d'autres l'ont dit, alors qu'il est légal de retour à partir d'un bloc finally selon la Java spec, c'est une chose MAUVAISE et ne devrait pas être fait.

94voto

Jason Cohen Points 36475

Les exemples que vous avez fournis sont une raison suffisante pour ne pas utiliser finalement le contrôle de flux.

Même s'il existe un exemple artificiel où il est "meilleur", considérons le développeur qui doit gérer votre code ultérieurement et qui peut ne pas être au courant des subtilités. Ce pauvre développeur pourrait même être toi ....

23voto

javac vous avertira du retour enfin si vous utilisez le -Xlint: enfin. À l'origine, javac n'émettait aucun avertissement - si quelque chose n'allait pas avec le code, la compilation ne devrait pas aboutir. Malheureusement, la compatibilité ascendante signifie que la folie ingénieuse imprévue ne peut être interdite.

Des exceptions peuvent enfin être levées à partir de blocs, mais dans ce cas, le comportement affiché est presque certainement ce que vous souhaitez.

13voto

iAn Points 3090

L'ajout de structures de contrôle et la retourne pour enfin{} blocs sont juste un autre exemple de "juste parce que vous pouvez" les abus qui sont dispersés dans pratiquement tous les langages de développement. Jason avait raison en suggérant qu'elle pourrait facilement devenir un entretien cauchemar - les arguments contre les premiers retours de fonctions s'appliquent plus à ce cas de "retards".

Enfin blocs existent pas pour un but, pour vous permettre de complètement nettoyer après vous-même, peu importe ce qui s'est passé dans tous les précédents code. Principalement il s'agit de clôture / libérant des pointeurs de fichier, de connexions de base de données, etc., même si j'ai pu le voir être étiré à-dire l'ajout en sur mesure audit.

Tout ce qui affecte le rendement de la fonction doit se situer dans le try{} bloc. Même si vous aviez une méthode par laquelle vous avez coché une extérieure de l'état, a fait un temps de l'opération, puis vérifié que l'état de nouveau dans le cas où il est devenu invalide, vous voulez toujours la deuxième case à l'intérieur de la try{} - si, il était assis à l'intérieur de finally{} et le long de l'échec de l'opération, vous devez alors vérifier l'état une deuxième fois inutilement.

6voto

Prof. Ondino Points 21

Un simple Groovy Test:

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)

Sortie:

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

Question:

Un point intéressant pour moi était de voir comment Groovy traite de rendement implicite. En Groovy, il est possible de "revenir" à partir d'une méthode laissant simplement une valeur à la fin (sans retour). Que pensez-vous qui se passe, si vous décommentez la runningThreads.supprimer(..) de la ligne dans l'instruction enfin est - ce que ce écraser le retour régulier de la valeur ("OK") et couvrez-le d'exception?!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X