finallyの罠

このレッスンでできるようになること。try や catch の return と finally の return がぶつかったとき、どちらの値が返るかを迷わず答えられるようになります。

まず予測してください

static int f() { try { return 1; } finally { return 2; } }

返るのは 2 です。try の return は「返す値を用意して、いったん脇に置いた」状態にすぎません。そこから finally が走り、finally が自分の return を実行すると、脇に置いた 1 は捨てられます。finally の return は try と catch の return をすべて上書きします。例外を投げていた場合も同じで、finally が return すると、その例外はもみ消されて呼び出し元まで届きません。

finally は必ず走る

finally は次のどの場合も実行されます。

  1. try が最後まで走り切ったとき
  2. try の途中で return したとき
  3. try で例外が飛んで catch で受けたとき
  4. try で例外が飛んで catch できなかったとき(この場合も finally のあとに例外が伝わります)

例外的に走らないのは System.exit が呼ばれたときなどですが、Silver では「finally は必ず走る」と覚えておけば足ります。

もうひとつの罠

finally で return しなければ、try の return 値は書き換わりません。

static int g() { int n = 1; try { return n; } finally { n = 99; // 返る値は 1 のまま } }

return n の時点で値 1 はコピー済みなので、あとから n を触っても遅いのです。ここを「99 が返る」と答えさせるのが定番の引っ掛けです。参照型でも同じで、変数に別のオブジェクトを代入し直しても返る先は変わりません。ただし同じオブジェクトの中身を書き換えた場合は、その変更は見えます

確かめる

演習では、try と catch と finally のどこを通ったかを1本の文字列に記録して返します。finally が return するときとしないときで、返る文字列がどう変わるかを実行して確かめてください。

要件

  1. try の中で log に "try" を足す
  2. n が 0 のときは ArithmeticException を投げて catch で受け、"-catch" を足した文字列を返す
  3. 例外が出なければ "-return" を足した文字列を返す
  4. finally では必ず "-finally" を足す
  5. n が 9 のときだけ finally が log.toString() を返す

入出力例

order(1)"try-return" order(0)"try-catch" order(9)"try-return-finally"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.java
学習モード

メモ

finallyの罠

⌘S で保存