public メソッドに指定されているかどうかにかかわらず、事後条件チェックはアサーションで実装するのが最適です。次に例を示します。
/** * Returns a BigInteger whose value is (this-1 mod m). * * @param m the modulus. * @return this-1 mod m. * @throws ArithmeticException m <= 0, or this BigInteger * has no multiplicative inverse mod m (that is, this BigInteger * is not relatively prime to m). */ public BigInteger modInverse(BigInteger m) { if (m.signum <= 0) throw new ArithmeticException("Modulus not positive: " + m); if (!this.gcd(m).equals(ONE)) throw new ArithmeticException(this + " not invertible mod " + m); ... // Do the computation assert this.multiply(result).mod(m).equals(ONE); return result; } |
実際には、2 番目の事前条件(this.gcd(m).equals(ONE)) は冗長であるので、計算を実行する前にチェックしません。この事前条件のチェックは、標準アルゴリズムによるモジュラ乗法逆数計算の副作用として行われます。
場合によっては、計算を実行する前にいくつかのデータを保存しておき、計算が完了した後に事後条件をチェックする必要があります。このような事後条件のチェックを行うには、2 つの assert 文と、計算の後にチェック (または再チェック) できるように 1 つまたは複数の変数の状態を保存するように設計された単純な内部クラスを使用します。たとえば、次のようなコードがあると仮定します。
void foo(int[] array) { // Manipulate array ... // At this point, array will contain exactly the ints that it did // prior to manipulation, in the same order. } |
次に、上記メソッドを変更して、形だけのアサーションから機能するアサーションに変更する方法を示します。
void foo(final int[] array) { class DataCopy { private int[] arrayCopy; DataCopy() { arrayCopy = (int[])(array.clone()); } boolean isConsistent() { return Arrays.equals(array, arrayCopy); } } DataCopy copy = null; // Always succeeds; has side effect of saving a copy of array assert (copy = new DataCopy()) != null; ... // Manipulate array assert copy.isConsistent(); } |
このメソッドを簡単に説明すると、複数のデータフィールドを保存して、計算前後の値に関連する複雑なアサーションを任意にテストします。
最初の assert 文 (副作用として単独で実行される) をよりわかりやすくすると、次のようになります。
copy = new DataCopy(); |
しかし、アサーションが有効であるかどうかにかかわらず、この文は配列をコピーするので、「無効である場合、アサーションは何にも影響してはならない」という規則に違反します。