web-dev-qa-db-ja.com

リターン後に仕事をするためのfinally句の使用は悪いスタイル/危険ですか?

イテレーターの作成の一環として、次のコード(エラー処理の除去)を作成していることに気付きました

public T next() {
  try {
    return next;
  } finally {
    next = fetcher.fetchNext(next);
  }
}

読むより少し読みやすい

public T next() {
  T tmp = next;
  next = fetcher.fetchNext(next);
  return tmp;
}

私はそれが読みやすさの違いがそれほど圧倒的ではないかもしれない単純な例であることを知っていますが、例外が含まれていないこのような場合にtry-finallyを使用することが悪いかどうかに関する一般的な意見に興味があります。コードを簡略化するときに実際に優先されるかどうか。

それが悪い場合:なぜ?スタイル、パフォーマンス、落とし穴、...?

結論ありがとうございました。 (少なくとも私にとって)結論は、最初の例は一般的なパターンであればもっと読みやすいかもしれないが、そうではないということだと思います。したがって、目的の外で構成を使用することにより生じる混乱と、難読化された例外フローが、単純化を上回ります。

30
Halle Knast

個人的に、私は コマンドクエリの分離 のアイデアを考えます。すぐに理解すると、next()には2つの目的があります。次の要素を取得するという宣伝されている目的と、nextの内部状態を変更することによる隠れた副作用です。したがって、あなたがやっていることは、メソッドの本体でアドバタイズされた目的を実行してから、finally句の隠された副作用に取り組むことです。

つまり、コードがいかに理解しやすいかということです。この場合、答えは「まあ」です。単純なreturnステートメントを "試行"してから、フェイルセーフエラー回復用と思われるコードブロックで隠れた副作用を実行しています。あなたがやっていることは「賢い」ことであり、「賢い」コードはしばしばメンテナーにつぶやくように誘導します。あなたのコードを読んでいる人々が「うん、うーん、理にかなっている、そう...」

では、状態変化をアクセサコールから分離するとどうなるでしょうか。あなたが懸念している読みやすさの問題が問題になると思いますが、それがあなたのより広い抽象化にどのように影響するのかはわかりません。とにかく、考慮すべきこと。

18
Erik Dietrich

純粋にスタイルの観点からは、次の3行が考えられます。

T current = next;
next = fetcher.getNext();
return current;

...は、try/finallyブロックよりもわかりやすく、短いです。例外がスローされることを想定していないため、tryブロックを使用すると、混乱するだけです。

37

それは、finallyブロック内のコードの目的に依存します。正規の例は、ストリームからの読み取り/書き込み後にストリームを閉じることです。これは、常に実行する必要がある一種の「クリーンアップ」です。オブジェクト(この場合はイテレーター)を有効な状態に復元すると、IMHOもクリーンアップとしてカウントされるため、ここでは問題は発生しません。 OTOHが戻り値が見つかった直後にreturnを使用していて、finallyブロックに多くの無関係なコードを追加すると、目的が不明確になり、すべてが理解しにくくなります。

「例外はありません」の場合は問題ありません。コードがcatchのみをスローでき、それらを処理する予定がない場合、RuntimeExceptionなしでtry...finallyを使用することは非常に一般的です。場合によっては、finallyは単なる保護手段であり、プログラムのロジック上、例外がスローされないことがわかっています(従来の「これは発生してはならない」条件)。

落とし穴:tryブロック内で例外が発生すると、finallyブロックが実行されます。これにより、一貫性のない状態になる可能性があります。したがって、returnステートメントが次のような場合:

return preProcess(next);

このコードで例外が発生しても、fetchNextは引き続き実行されます。 OTOHを次のようにコーディングした場合:

T ret = preProcess(next);
next = fetcher.fetchNext(next);
return ret;

その後、実行されません。私はあなたがtryコードがneverで例外を発生させることができると想定していることを知っていますが、これよりも複雑な場合はどのように確認できますか?イテレータが長期間有効なオブジェクトである場合、現在のスレッドで回復不可能なエラーが発生した場合でも、それが存在し続けるため、常に有効な状態に保つことが重要です。それ以外の場合は、それほど問題ではありません...

パフォーマンス:このようなコードを逆コンパイルして内部でどのように動作するかを確認するのは興味深いことですが、JVMが十分に推測できるほどよくわかりません...例外は通常「例外的」なので、処理するコードはそれらは速度を最適化する必要はありません(したがって、プログラムの通常の制御フローで例外を使用しないようにアドバイスします)が、finallyについては知りません。

6
mgibsonbr

タイトルステートメント:「...帰国後に作業を行うための最後の句...」は誤りです。関数が戻る前に、finallyブロックが発生します。それが実際にようやく重要なポイントです。

ここで悪用しているのは、評価の順序です。nextの値は、変更する前に戻るために格納されます。それは一般的な習慣ではなく、私の意見では間違っているとコードが非順次になり、従って追跡するのがはるかに難しくなるからです。

Finallyブロックの使用目的は、スローされた例外に関係なく何らかの結果が発生する必要がある例外処理用です。リソースのクリーンアップ、DB接続のクローズ、ファイル/ソケットのクローズなど。

3
Nitsan Wakart

(一般的には、あなたの例ではありませんが)tryの一部が例外をスローする可能性があるため、私は本当に注意深くなります。スローされた場合、何も返されず、実際に最終的に返された後に実行されているものを実行しようとすると、実行したくないときに実行します。

そして、他のワームの缶は、一般的に、いくつかの便利なアクションが最終的に例外をスローする可能性があるため、本当に厄介な方法で終了することができます。

これらの理由から、それを読んでいる人はこれらの問題について考えなければならないので、理解するのは困難です。

0
user470365