例外処理(try,catch,finally)を記述する基本原則 - Javaとかで学ぶ、すぐに使いたくなる洗練されたプログラムイディオム集例外処理(try,catch,finally)は、if文やfor文などと比べ、歴史の浅い制御文だ。歴史が浅い分、例外処理の書き方も確立・定着しておらず、人によってまちまちだ。そこで今回は例外処理の書き方を取り上げる。 例外処理をまじめに実装するとかなり複雑なことになる。そのため今回はシンプルにするイディオムというよりも、シンプルな基本原則を示すことにする。 例外処理の基本原則
次のようなコードは、不適切にもかかわらず意外とよく見かける。 1static void writeTodayかなりいまいち(File file) throws IOException { 2 try { 3 OutputStream os = new FileOutputStream(file); 4 OutputStreamWriter writer = new OutputStreamWriter(os, "UTF8"); 5 writer.write(new SimpleDateFormat("yyyy/MM/dd") 6 .format(new Date())); 7 writer.close(); // 上で例外が発生すると実行されない 8 os.close(); 9 } catch (IOException e) { // 非検査例外(RuntimeExceptionなど)のときに実行されない 10 file.delete(); // fileに書き込み権はないが削除権がある場合、osのオープンに成功したときのみ実行したいという意図に反して削除される。 11 logger.log(Level.SEVERE, e.getMessage(), e); // ログの出力が重複している 12 throw e; 13 } 14} 15 16static void writeTodayCallerかなりいまいち() { 17 File file = new File("c:/tmp/tempfile.txt"); 18 try { 19 writeTodayかなりいまいち(file); 20 } catch (IOException e) { 21 logger.log(Level.SEVERE, e.getMessage(), e); 22 } 23} 24 次のコードはかなりいいところまでいっているが、最後の基本原則が適用されていない。 1static void writeTodayいまいち(File file) throws IOException { 2 boolean done = false; // しばしばこうした変数の導入が必要になる 3 OutputStream os = new FileOutputStream(file); 4 try { 5 OutputStreamWriter writer = new OutputStreamWriter(os, "UTF8"); 6 try { 7 writer.write(new SimpleDateFormat("yyyy/MM/dd") 8 .format(new Date())); 9 done = true; 10 } finally { 11 writer.close(); 12 } 13 } finally { 14 os.close(); 15 if (!done) { 16 file.delete(); // 上のcloseで例外が発生すると実行されない 17 } 18 } 19} 20 21static void writeTodayCallerいまいち() { 22 try { 23 writeTodayいまいち(new File("c:/tmp/tempfile.txt")); 24 } catch (IOException e) { 25 logger.log(Level.SEVERE, e.getMessage(), e); 26 } 27} 28 インデントが凸凹して複雑な構造になるが、以下が正しい例外の実装になる。 1static void writeToday正解(File file) throws IOException { 2 boolean done = false; 3 OutputStream os = new FileOutputStream(file); 4 try { 5 OutputStreamWriter writer = new OutputStreamWriter(os, "UTF8"); 6 try { 7 writer.write(new SimpleDateFormat("yyyy/MM/dd") 8 .format(new Date())); 9 done = true; 10 } finally { 11 writer.close(); 12 } 13 } finally { 14 try { 15 os.close(); 16 } finally { 17 if (!done) { 18 file.delete(); 19 } 20 } 21 } 22} 23 24static void writeTodayCaller正解() { 25 try { 26 writeToday正解(new File("c:/tmp/tempfile.txt")); 27 } catch (IOException e) { 28 logger.log(Level.SEVERE, e.getMessage(), e); 29 } 30} 31 |