必ずファイルをクローズする全行処理メソッド
http://d.hatena.ne.jp/t_yano/20061002/1159812712
http://d.hatena.ne.jp/t_yano/20061004/1159987463
http://d.hatena.ne.jp/yojik/20061007/1160158749
http://d.hatena.ne.jp/odz/20061006/1160168799
面白そうだったので、自分でもやってみた。
みんなの議論のいいとこ取り(のはず^^;)。
まず、Closure だけど、execute() に throws Exception をつけて、検査例外があげられるようにした。
onError() 改め handleException() はデフォルト再スローの方が良いと思う。
odzさんのエントリで、dispatchErrorHandler() と onError() を分けた理由、リフレクションを使った理由はよくわからなかった。
// Closure.java public abstract class Closure{ // throws Exception を追加。 public abstract void execute(T input) throws Exception; // onError() から名前変更。 // デフォルトの実装を変えた。 public void handleException(Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException)e; } else { throw new CheckedException(e); } } // continue も追加。 protected final void continueLoop() { throw new ContinueException(); } // stop() から名前変更。 protected final void breakLoop() { throw new BreakException(); } } class ContinueException extends RuntimeException {} class BreakException extends RuntimeException {}
検査例外のラッパークラス。
// CheckedException.java public class CheckedException extends RuntimeException { public CheckedException(Exception e) { super(e); } }
File のサブクラスを作るより、Reader にした方が使い勝手がいいんじゃないかな?
eachLine() で、Closure の処理の外で発生する IOException はそのままスロー。
while 文周辺のロジックを整理した。
// IterableReader.java public class IterableReader { private BufferedReader reader; public IterableReader(File file) throws FileNotFoundException { this(toBufferedReader(file)); } public IterableReader(Reader reader) { this(toBufferedReader(reader)); } public IterableReader(BufferedReader reader) { this.reader = reader; } private static BufferedReader toBufferedReader(File file) throws FileNotFoundException { return toBufferedReader(new FileReader(file)); } private static BufferedReader toBufferedReader(Reader reader) { if (reader instanceof BufferedReader) { return (BufferedReader)reader; } else { return new BufferedReader(reader); } } public void eachLine(Closureproc) throws IOException { try { String line = reader.readLine(); while (line != null) { try { proc.execute(line); } catch (ContinueException e) { continue; } catch (BreakException e) { break; } catch (Exception e) { proc.handleException(e); } line = reader.readLine(); } } finally { close(); } } protected void close() throws IOException { reader.close(); Logger.global.info("**** Reader has been closed."); } }
使用例。まずは、途中で break する場合。
検査例外をあげないなら execute() に throws をつける必要はなし。
public class Example { public static void main(String[] args) throws Exception { //ファイルを作って File file = new File("Example.java"); //一行ずつ処理する new IterableReader(file).eachLine(new Closure() { public void execute(String line) { System.out.println(line); if (line.startsWith("p")) breakLoop(); } }); //ここではもう閉じられている。 } }
続いて、途中で例外をあげる場合。
execute() で必要な例外を throws 指定する。検査例外は CheckedException で受ける。
public class Example2 { public static void main(String[] args) throws Exception { //ファイルを作って File file = new File("Example2.java"); //一行ずつ処理する try { new IterableReader(file).eachLine(new Closure() { public void execute(String line) throws Exception { System.out.println(line); if (line.startsWith("p")) throw new Exception(); } }); } catch (CheckedException e) { e.getCause().printStackTrace(); } //ここではもう閉じられている。 } }
以上。
矢野さんの
> もうネタのようなクラスとインターフェースができました。
が気になるなぁ...