Javaでusingブロックもどき(JavaSE7)

Javaのファイナライザ微妙だよな−、という話から発展して、JavaSE7のクロージャでC#のusingブロックのようなものが実現できるようになるかも、という話をしたので、それもついでに補足。
ここで紹介するサンプルは、Closures (Lambda Expressions) for the Java Programming Languageの2008-08-11バージョンで動作確認済み。


【コラム】Java API、使ってますか? (58) Java SE 7の要注目機能"クロージャ"はどうなるのか その6 | 開発・SE | マイナビニュース
を参考に、最大4つのCloseableオブジェクトに対応。

public static <R, T extends Closeable, throws E>
R using(T t, { T ==> R throws E } block) throws E, IOException {
    try {
        return block.invoke(t);
    } finally {
        if (t != null) t.close();
    }
}

public static <R, T1 extends Closeable, T2 extends Closeable, throws E>
R using(T1 t1, T2 t2, { T1, T2 ==> R throws E } block) throws E, IOException {
    try {
        return block.invoke(t1, t2);
    } finally {
        closeAll(t1, t2);
    }
}

public static <R, T1 extends Closeable, T2 extends Closeable, T3 extends Closeable, throws E>
R using(T1 t1, T2 t2, T3 t3, { T1, T2, T3 ==> R throws E } block) throws E, IOException {
    try {
        return block.invoke(t1, t2, t3);
    } finally {
        closeAll(t1, t2, t3);
    }
}

public static <R, T1 extends Closeable, T2 extends Closeable, T3 extends Closeable, T4 extends Closeable, throws E>
R using(T1 t1, T2 t2, T3 t3, T4 t4, { T1, T2, T3, T4 ==> R throws E } block) throws E, IOException {
    try {
        return block.invoke(t1, t2, t3, t4);
    } finally {
        closeAll(t1, t2, t3, t4);
    }
}

private static void closeAll(Closeable...resources) throws IOException {
    IOException exception = null;
    for (Closeable resource : resources) {
        try {
            if (resource != null) resource.close();
        } catch (IOException e) {
            if (exception == null) exception = e;
        }
    }
    if (exception != null) throw exception;
}

クロージャを最後の引数としてとる必要があるため、可変引数が使えないのが難点かな。
closeAllは正直微妙。


使い方はこんな感じ。

using (BufferedReader in, BufferedWriter out :
        new BufferedReader(new FileReader("Main.java")),
        new BufferedWriter(new FileWriter("out.txt"))) {
    String line;
    while ((line = in.readLine()) != null) {
        System.out.println(line);
        out.write(line);
        out.newLine();
    }
}


出来ればこんな感じに書きたかった。

using (BufferedReader in : new BufferedReader(new FileReader("Main.java")),
       BufferedWriter out : new BufferedWriter(new FileWriter("out.txt"))) {
    // ...
}