Question

J'ai été surpris récemment de constater qu'il est possible d'avoir une instruction return dans un bloc final en Java.

Il semble que beaucoup de gens pensent que c'est une mauvaise chose de faire ce qui est décrit dans "Ne revenez pas dans une clause finale'.En grattant un peu plus profondément, j'ai aussi trouvé 'Le retour de Java n'est pas toujours le cas' qui montre des exemples assez horribles d'autres types de contrôle de flux dans les blocs final.

Donc, ma question est la suivante : quelqu'un peut-il me donner un exemple dans lequel une instruction return (ou un autre contrôle de flux) dans un bloc final produit un code meilleur/plus lisible ?

Était-ce utile?

La solution

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

Même s'il existe un exemple artificiel où c'est "meilleur", pensez au développeur qui devra maintenir votre code plus tard et qui pourrait ne pas être au courant des subtilités.Ce pauvre développeur pourrait même être vous...

Autres conseils

J'ai eu VRAIMENT du mal à détecter un bug il y a des années qui était causé par cela.Le code ressemblait à ceci :

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 un autre code.Il était capturé, enregistré et relâché dans le somethingThatThrewAnException() méthode.Mais l'exception ne se propageait pas au-delà problemMethod().Après une LONGUE période d'examen, nous avons finalement retrouvé la méthode de retour.La méthode de retour dans le bloc final empêchait essentiellement l'exception survenue dans le bloc try de se propager même si elle n'avait pas été interceptée.

Comme d'autres l'ont dit, même s'il est légal de revenir d'un bloc final selon la spécification Java, c'est une MAUVAISe chose et ne devrait pas être fait.

javac vous avertira du retour final si vous utilisez -Xlint:finally.À l'origine, javac n'émettait aucun avertissement - si quelque chose ne va pas avec le code, la compilation devrait échouer.Malheureusement, la rétrocompatibilité signifie que les bêtises ingénieuses imprévues ne peuvent être interdites.

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

L'ajout de structures de contrôle et de retours aux blocs enfin{} n'est qu'un autre exemple d'abus "juste parce que vous pouvez" qui sont dispersés dans pratiquement tous les langages de développement.Jason avait raison de suggérer que cela pourrait facilement devenir un cauchemar de maintenance - les arguments contre les retours précoces des fonctions s'appliquent davantage à ce cas de "retours tardifs".

Enfin, les blocs existent dans un seul but, vous permettre de faire complètement le ménage derrière vous, peu importe ce qui s'est passé dans tout le code précédent.Il s'agit principalement de fermer/libérer des pointeurs de fichiers, des connexions à des bases de données, etc., même si je pouvais voir que cela s'étendait pour dire l'ajout d'un audit sur mesure.

Tout ce qui affecte le retour de la fonction doit se trouver dans le bloc try{}.Même si vous disposiez d'une méthode par laquelle vous vérifiiez un état externe, effectuiez une opération fastidieuse, puis vérifiiez à nouveau cet état au cas où il deviendrait invalide, vous voudriez toujours la deuxième vérification à l'intérieur du try{} - s'il restait finalement à l'intérieur{} et que la longue opération échouait, vous vérifieriez alors cet état une deuxième fois inutilement.

Un simple 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)

Sortir:

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 gère les retours implicites.Dans Groovy il est possible de "revenir" d'une méthode en laissant simplement une valeur à la fin (sans retour).Selon vous, que se passe-t-il si vous décommentez le runningThreads.remove(..) ligne dans l'instruction finale - cela écrasera-t-il la valeur de retour normale ("OK") et couvrira-t-il l'exception ?!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top