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 は次のどの場合も実行されます。
- try が最後まで走り切ったとき
- try の途中で return したとき
- try で例外が飛んで catch で受けたとき
- 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 するときとしないときで、返る文字列がどう変わるかを実行して確かめてください。
要件
- try の中で log に "try" を足す
- n が 0 のときは ArithmeticException を投げて catch で受け、"-catch" を足した文字列を返す
- 例外が出なければ "-return" を足した文字列を返す
- finally では必ず "-finally" を足す
- n が 9 のときだけ finally が log.toString() を返す
入出力例
order(1) → "try-return"
order(0) → "try-catch"
order(9) → "try-return-finally"ヒント
編集 LuaGate編集部