184

最近、Java の finally ブロックに return ステートメントを含めることができることに驚きました。

「 finally 句で返さない」で説明されているように、多くの人が悪いことだと考えているようです。もう少し掘り下げてみると、「Java's return does not always」も見つかりました。これは、finally ブロックでの他のタイプのフロー制御の非常に恐ろしい例を示しています。

それで、私の質問は、finally ブロックの return ステートメント (または他のフロー制御) がより優れた/より読みやすいコードを生成する例を誰か教えてもらえますか?

4

6 に答える 6

160

何年も前に、これによって引き起こされたバグを追跡するのに本当に苦労しました。コードは次のようなものでした:

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

何が起こったのかというと、例外が他のコードでスローされたということです。somethingThatThrewAnException()メソッド内でキャッチされ、ログに記録され、再スローされていました。しかし、例外は過去に伝播されていませんでしたproblemMethod()。これを長い間調べた後、最終的に return メソッドにたどり着きました。finally ブロックの return メソッドは、基本的に、try ブロックで発生した例外がキャッチされなくても伝播するのを止めていました。

他の人が言ったように、Java仕様に従ってfinallyブロックから戻ることは合法ですが、それは悪いことであり、行うべきではありません.

于 2008-09-07T20:17:35.007 に答える
94

あなたが提供した例は、finally からのフロー制御を使用しない十分な理由です。

「より良い」という不自然な例があったとしても、後でコードを保守する必要があり、微妙な点に気付いていない可能性のある開発者を考えてみてください。その貧しい開発者はあなたかもしれません....

于 2008-09-07T03:20:29.397 に答える
23

-Xlint:finally を使用すると、javac は finally に戻ることを警告します。もともと javac は警告を発しませんでした。コードに何か問題がある場合は、コンパイルに失敗するはずです。残念ながら、下位互換性があるということは、予期せぬ巧妙な愚かさを禁じることができないことを意味します。

例外は finally ブロックからスローされる可能性がありますが、その場合、示された動作はほぼ確実にあなたが望むものです。

于 2008-09-07T13:50:01.473 に答える
13

制御構造を追加してfinally{}ブロックに戻ることは、事実上すべての開発言語に散在する「できるという理由だけで」悪用のもう1つの例です。ジェイソンは、それが簡単にメンテナンスの悪夢になる可能性があることを示唆していました-関数からの早期リターンに対する議論はもっと当てはまります-したがって、この「遅いリターン」の場合に当てはまります。

最後に、ブロックは1つの目的のために存在します。これにより、前のすべてのコードで何が起こったとしても、自分の後で完全に片付けることができます。これは主に、ファイルポインター、データベース接続などを閉じたり解放したりすることですが、特注の監査を追加すると言っても過言ではありません。

関数の戻りに影響を与えるものはすべて、try{}ブロックにある必要があります。外部状態をチェックし、時間のかかる操作を行ってから、無効になった場合に備えてその状態を再度チェックする方法がある場合でも、try{}内で2回目のチェックが必要になります。長い操作が失敗した場合は、その状態をもう一度不必要にチェックすることになります。

于 2008-09-07T03:31:57.373 に答える
6

簡単な 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)

出力:

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

質問:

私にとって興味深い点の 1 つは、Groovy が暗黙の戻り値をどのように処理するかを確認することでした。Groovy では、メソッドの最後に値を残すだけで (return なしで)、メソッドから「戻る」ことができます。finally ステートメントのrunningThreads.remove(..)行のコメントを外すと、何が起こると思いますか?これにより、通常の戻り値 ("OK") が上書きされ、例外がカバーされますか?!

于 2011-03-04T16:44:06.097 に答える
2

finallyブロック内から戻るexceptionsと失われます。

finally ブロック内の return ステートメントにより、try または catch ブロックでスローされる可能性のある例外が破棄されます。

Java 言語仕様によると:

try ブロックの実行が R 以外の理由で突然終了した場合、finally ブロックが実行され、次の選択肢があります。

   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).

注: JLS 14.17によると、return ステートメントは常に突然完了します。

于 2020-02-08T06:59:21.213 に答える