finally の return が結果を上書きする問題
finallyでreturnを書いてはいけない理由と、try-with-resourcesによる安全なリソース解放を扱います。
なぜエラーが出ないのか
出力: 2
(エラーなし)- javacは何も報告しません。文法として正しいためです
出力: 2- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 public class Main { 2 static int f() { 3 try { return 1; } 4 finally { return 2; } ^ 5 } 6 public static void main(String[] args) { 7 System.out.println(f()); 8 } 9 }
出力: 2期待: 1 (コンパイルは通っています)
finally内のreturnはtry内のreturnを上書きします。finallyでreturnを書かないのが鉄則です。
直し方: finally を catch (Exception e) にします。
広告
広告スロット(未設定)
パターン2
1 import java.io.*; 2 3 public class Main { 4 public static void main(String[] args) throws IOException { 5 try (String r = new BufferedReader(new StringReader("a"))) { ^ 6 System.out.println(r.readLine()); 7 } 8 } 9 }
Main.java:5: error: incompatible types: BufferedReader cannot be converted to String
try-with-resourcesの変数はAutoCloseableを実装した型で宣言します。ブロックを抜けると自動でcloseされます。
直し方: String を BufferedReader にします。
パターン3
1 public class Main { 2 public static void main(String[] args) { 3 try { 4 System.out.println("try"); 5 } { ^ 6 System.out.println("always"); 7 } 8 } 9 }
Main.java:5: error: 'try' without 'catch', 'finally' or resource declarations
tryは単独では書けません。catchかfinally、またはリソース宣言のいずれかが必要です。
直し方: (空) を finally にします。
よくある誤解
finallyは必ず実行されると言われますが、System.exit() を呼んだ場合は実行されません。
まとめ
finally の return が結果を上書きする問題は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。