読者です 読者をやめる 読者になる 読者になる

【Effective Java】項目64:エラーアトミック性につとめる

Java Effective Java

オブジェクトのメソッドが例外をスローしたあとは、そのメソッドを呼び出す前の状態になっているべきです。 この性質を「エラーアトミックである」といいます。

「エラーアトミック」を達成する方法はいくつかあります。

不変オブジェクト

エラーアトミックを達成するいちばん簡単な方法は不変オブジェクトを使うことです。

不変オブジェクトはオブジェクト作成後に状態が変化しませんので、メソッドで例外がスローしたとしても状態は変化しません。

パラメータチェック

次に簡単な方法はメソッド内でオブジェクトの状態を変更する前に、各種パラメータチェックを行うことです。

簡単なスタック実装の pop() メソッドを例にとります。

public Object pop() {
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

この pop() 実装では、size が 0 よりも下回ると IndexOutOfBoundsExeption が発生して、例外がスローされます。 このとき、size の値は 0 を下回ったままですので不整合な状態になってしまいます。

そこで、次のように事前にパラメータチェックを行います。

public Object pop() {
    if (size == 0) {
        throw new EmptyStackException();   
    }
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

このように実装すれば、例外がスローされても状態は変化しませんし、抽象概念の正しい例外を発生させることもできます(項目61)。

状態変更前の事前計算

オブジェクトを変更する前に必要な計算を行ってしまって、不整合を起こす状態になったら例外を発生させてしまう方法です。

この方法でエラーアトミック性を実現しているのは TreeMap です。

TreeMap に要素を追加するためには、オブジェクトに「順序付け可能」という性質が必要です。 TreeMap にオブジェクトを要素に追加しようとすると、まずオブジェクトの検索が行われます。

この検索は木構造にオブジェクトが追加される前に行われ、もし順序づけが不可能であればこの時点で例外が発生します。 木構造にオブジェクトが追加される前に例外がスローされるため、木構造は不整合な状態になりません。

回復コードを書く

例外が発生した際に、状態を復元する回復コードを書くという方法です。 例外を発生させる処理をする前の状態を保存しておく必要があるでしょう。

コピーを作る

例外が発生する処理を行う前にコピーオブジェクトを作成し、それを利用する方法です。 操作はすべてこのコピーオブジェクトに対して行い、すべての操作が成功した段階で本当のオブジェクトに付け替えるという方法です。

エラーアトミックの限界

エラーアトミック性は望ましいことですが、複雑性・コストを増大させることが多いので、必ずしも望ましいとは限りません。

もし、エラーアトミック性が保てない場合は、かならず仕様の一部としてドキュメント化するべきです。