凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 260
  • 記事 - 0
  • コメント - 46991
  • トラックバック - 192
ニュース
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
  • あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

完璧というのはなかなか難しいもので、時が経つとほころびを見つけてしまうものです。

先のエントリでは 異常処理のほころびを指摘されたので、JavaでI/Oをいじるさいの完璧な例外処理とはどのようなものか考えて見ました。

public void hoge() throws IOException {
    boolean returnFlag = false;
    boolean throwFlag = false;
    OutputStream out = null;
    try {
        // ストリームのオープン
        out = new FileOutputStream("test.txt")// (A)

        // 書き込み
        out.write(0)// (B)
        if (returnFlag) {
            return// (C)
        }
        if (throwFlag) {
            throw new NullPointerException()// (D)
        }

        // 正常系の後始末
        out.close()// (E)
        out = null;
    finally {
        if (out != null) {
            // outが有効ならclose()を試みる
            out.close()// (ZA)
        else {
            // outが無効なら何もしない
            // (ZB)
        }
    }
}

まずは、上記のようなコードを書いてみました。通過ルートとしては

  • 異常系 : A:IOException → ZB → A:IOException
  • 異常系 : A → B:IOException → ZA → B:IOException
  • 異常系 : A → B:IOException → ZA → ZA:IOException
  • 正常系 : A → B → C → ZA → return
  • 異常系 : A → B → C → ZA → ZA:IOException
  • 異常系 : A → B → D:NullPointerException → ZA → D:NullPointerException
  • 異常系 : A → B → D:NullPointerException → ZA → ZA:IOException
  • 正常系 : A → B → E → ZB → return
  • 異常系 : A → B → E:IOException → ZA → E:IOException
  • 異常系 : A → B → E:IOException → ZA → ZA:IOException

が考えられます。
このソースでは、finallyでのclose()の際に発生したIOExceptionもメソッドからthrowされて 呼び元に通知する仕掛けなのですが、で示した箇所では情報が欠損します。
本来の例外はきえてなくなり、close()で発生したIOExceptionに乗っ取られるのです。

そこでこれらの部分で例外のチェーンを用いて情報が欠損しないようにしてみたいと思います。

public void piyo() throws IOException {
    boolean returnFlag = false;
    boolean throwFlag = false;
    OutputStream out = null;
    Throwable throwable = null;
    try {
        // ストリームのオープン
        out = new FileOutputStream("test.txt")// (A)

        // 書き込み
        out.write(0)// (B)
        if (returnFlag) {
            return// (C)
        }
        if (throwFlag) {
            throw new NullPointerException()// (D)
        }

        // 正常系の後始末
        out.close()// (E)
        out = null;
    catch (IOException ioe) {
        throwable = ioe; // (XA)
        throw ioe;
    catch (RuntimeException e) {
        throwable = e; // (XB)
        throw e;
    finally {
        if (out != null) {
            // outが有効ならclose()を試みる
            try {
                out.close()// (ZA)
            catch (IOException ioe) {
                // finallyのclose()で例外が発生した場合、例外チェーンとする
                if (throwable != null) {
                    ioe.initCause(throwable)// (ZA')
                }
                throw ioe; // (ZA'')
            }
        else {
            // outが無効なら何もしない
            // (ZB)
        }
    }
}

これにより、通過ルートは以下のようになります。

  • 異常系 : A:IOException → ZB → A:IOException
  • 異常系 : A → B:IOException → XA → ZA → B:IOException
  • 異常系 : A → B:IOException → XA → ZA → ZA' → ZA'' → ZA:IOException(B:IOException)
  • 正常系 : A → B → C → ZA → return
  • 異常系 : A → B → C → ZA → ZA'' → ZA:IOException
  • 異常系 : A → B → D:NullPointerException → XB → ZA → D:NullPointerException
  • 異常系 : A → B → D:NullPointerException → XB → ZA → ZA' → ZA'' → ZA:IOException(D:NullPointerException)
  • 正常系 : A → B → E → ZB → return
  • 異常系 : A → B → E:IOException → XA → ZA → E:IOException
  • 異常系 : A → B → E:IOException → XA → ZA → ZA' → ZA'' → ZA:IOException(E:IOException)

finally内ではtry-catch ブロックで宣言された変数にアクセスできませんから、えらく面倒な作りになってしまいました。 ここまでしないと駄目なのでしょうか。言語のサポートも欲しいところですね。

投稿日時 : 2007年8月6日 18:04
コメント
タイトル
名前
Url
コメント