CUBは子供の白熊

Java SE 8 実践プログラミングの練習問題を解く

第9章 Java 7 の機能を復習する : 問題 2 : 抑制された例外

問題

以下のコードを try-with-resources 文を使わず、かつcloseメソッドでスローされる例外を抑制された例外として元の例外に追加するように実装せよ。

■ try-with-resources 文を使った例

try (BufferedReader in = Files.newBufferedReader(Paths.get("/usr/share/dict/words"));
     BufferedWriter out = Files.newBufferedWriter(Paths.get("/tmp/out.txt"))
    ) {
    String line;
    while ((line = in.readLine()) != null) {
        out.write(line.toLowerCase());
        out.newLine();
    }
}

解答

IOExceptionがスローされるのは、大きく分けて以下の4つのケースである。

  • inないしoutのオープン
  • ファイルを読み書きするメインパート
  • outのクローズ
  • inのクローズ

そこで、これら4つのケースで発生した例外を覚えておくことにする。

IOException openException = null;     // リソースのオープンで発生した例外
IOException mainException = null;     // 中心となる例外
IOException inCloseException = null;  // in.close() で発生した例外
IOException outCloseException = null; // out.close() で発生した例外

try {
    BufferedReader in;
    try {
        in = Files.newBufferedReader(Paths.get("/usr/share/dict/words"));
    } catch (IOException ex) {
        openException = ex;
        throw ex;
    }
    try {
        BufferedWriter out;
        try {
            out = Files.newBufferedWriter(Paths.get("/tmp/out.txt"));
        } catch (IOException ex) {
            openException = ex;
            throw ex;
        }
        try {
            String line;
            while ((line = in.readLine()) != null) {
                out.write(line.toLowerCase());
                out.newLine();
            }
        } catch (IOException ex) {
            mainException = ex;
            throw ex;
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
                outCloseException = ex;
                throw ex;
            }
        }
    } finally {
        try {
            in.close();
        } catch (IOException ex) {
            inCloseException = ex;
            throw ex;
        }
    }
} catch (IOException ex) {
    // ex は openException, mainException, inCloseException, outCloseException のいずれか
    if (openException != null) {
        // mainException, outCloseException は発生していない
        if (inCloseException != null)
            openException.addSuppressed(inCloseException);
        throw openException;
    }
    if (mainException != null) {
        if (inCloseException != null)
            mainException.addSuppressed(inCloseException);
        if (outCloseException != null)
            mainException.addSuppressed(outCloseException);
        throw mainException;
    }
    if (inCloseException != null && outCloseException != null) {
        inCloseException.addSuppressed(outCloseException);
        throw inCloseException;
    }
    throw ex;
}

なんというステップ数、可読性は最悪。
try-with-resources 文は偉大だ!