リフレクションで適当に例外ハンドリング

で、話は戻ります。例えば上記の日記には「検査例外を呼び手に上手く返せない」という突っ込みが入っていますが、Closureとして渡す匿名クラス自身にerrorハンドル用のメソッドつければいいんじゃね、と思った。try catch使うよりスッキリするはず。(考えているネタと被ったらごめんなさい)

呼び出し側ですぐに catch するならそれでもいいんだけど、実際にはスタックフレームのもっと浅い側で catch したい場合も多いので微妙なところ。
でだ、

abstract class Closure<T> {
  abstract public void execute(T value);
  public void onError(Exception e){}                 //デフォルト実装与えてみました
  public void stop() {throw new BreakException();}    //STOP機能付けてみました。
}

このインターフェースだと execute に throws の宣言が付いていないので、どっちにしても検査例外を送出できないんですけど。検査例外と実行時例外の違いを考えてほすぃ。
んで、どうせ匿名クラスで例外処理をするならリフレクション使って dispatch してみてもいいんじゃね、とか思った。

abstract class Closure<T> {
    public abstract void execute(T value) throws Exception;

    public void stop() {
        throw new BreakException();
    }
    
    public void dispatchErrorHandler(Exception e) throws Exception {
        try {
            Method handler = this.getClass().getDeclaredMethod("onError", e.getClass());
            handler.invoke(this, e);
        } catch (NoSuchMethodException ex) {
            throw e;
        }
    }
}

で、eachLine を下記のようにしておいて、

    public void eachLine(Closure<String> proc) throws IOException {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new FileReader(this));
            String line;
            while ((line = in.readLine()) != null) {
                try {
                    proc.execute(line);
                } catch (Exception ex) {
                    try {
                        proc.dispatchErrorHandler(ex);
                        break;
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } catch (BreakException ex) {
        } finally {
            IOUtils.closeQuietly(in);
        }
    }

使うときはこんな感じ。

        final Random r = new Random();
        new IterableFile(args[0]).eachLine(new Closure<String>() {
            public void execute(String line) throws Exception {
                System.out.println(line);
                
                if (r.nextInt(100) == 0) {
                    throw new IOException();
                }
            }
            
            @SuppressWarnings("unused")
            public void onError(IOException ex) throws Exception {
                Logger.global.info("handled IOException");
            }
        });

匿名クラスで捕捉されなかった例外は RuntimeException に変換して送出と。
onError を typo したときに気づきにくいし、例外捕捉しないで呼び出し側に任せたい場合にはやっぱり駄目だから微妙だけどな。
ということで、矢野さんのネタとかぶらないことを祈りつつポスト。

補足

odzさんのエントリで、dispatchErrorHandler() と onError() を分けた理由、リフレクションを使った理由はよくわからなかった。

しまった、説明が足りなかった。わざわざ分けたのは投げる例外の種類が2種類以上あるときに、いちいち instanceof で dispatch するのがいやだったから。
こんな感じ。

final DocumentBuilder parser = 
    DocumentBuilderFactory.newInstance().newDocumentBuilder();
new IterableFile(args[0]).eachLine(new Closure<String>() {
    public void execute(String line) throws Exception {
        parser.parse(line);
    }

    public void onError(SAXException ex) throws Exception {
        System.err.println("XML pars error");
        System.exit(1);
    }

    public void onError(IOException ex) throws Exception {
        System.err.println("IO Error occured");
        System.exit(1);
    }
});

追記

よくよく考えれば instanceof 使わなくても

    public void onError(Exception e) {
        try {
            throw e;
        } catch (SAXException ex) {
        } catch (IOExcption ex) {
        }
    }

なんてものありだな。きもいけど。