連載
» 2004年10月27日 10時00分 公開

JavaTips 〜Javaプログラミング編:プログラムの品質を高めるためのアサーションとは?

[BULL,@IT]

契約による設計(Design by Contract)

 プログラムの品質向上手法として、契約による設計(Design by Contract、以下DBC)という手法があります。DBCでは、あるオブジェクトに対するメソッド呼び出しを行っているとき、プログラムが正しく動作している際に満たされるべき条件として、以下の3条件を規定しておきます。

  • 事前条件…… メソッド呼び出し時の引数が満たすべき条件
  • 事後条件…… メソッドによる処理が終了した時点で満たすべき条件
  • 不変条件…… メソッドによる処理により変化しないものを規定する条件

 以上の3条件のうちいずれかの条件が満たされない場合、プログラムは正しく動作していない、つまりバグがあることになります。

 例として、ある実数xの平方根yを計算するメソッドを作成する場合の事前条件と事後条件について考えます。実数xが0以上の値でなければ平方根を求めることができないため、xが0以上というのが事前条件になります。また、平方根の値は必ず0以上になるため、yが0以上というのが事後条件になります。事前条件を満たさない事態が発生した場合は、メソッドの呼び出し元にバグがあることになりますし、事後条件を満たさない事態が発生した場合は、平方根を求めるメソッド自身にバグがあることになります。このように、DBCを用いるとバグの原因を追究しやすくなり、結果としてプログラムの品質を向上させることができます。

アサーションの概要

 J2SDK 1.4からはアサーションが使用可能になりました。アサーションはある条件をチェックし、条件が偽になったときにAssertionError例外を発生させる構文です。アサーションは実行時に有効・無効にするかを決定できるため、デバッグ時にはアサーションを有効にしてデバッグを容易にし、リリース時には無効にして処理速度を向上させる、といったことが可能になります。DBCの手法をJavaに適用する場合は、条件設定としてアサーションを用いると便利です。

 アサーションは以下のいずれかの書式で記述します。

assert 条件;
assert 条件 : 条件が偽になった際に表示される文字列;


 「条件が偽になった際に表示される文字列」として、String以外のオブジェクトを記述した場合は、当該オブジェクトのtoString()メソッドの戻り値が表示される文字列になります。

 アサーションを有効にするには、コンパイル時のjavac、および実行時のjavaコマンドでオプションを指定する必要があります。コンパイル時には「-source 1.4」オプションを追加します。このオプションを追加しないとコンパイル時にアサーションが取り除かれます。コンパイル時にオプションを追加した例を以下に示します。

javac -source 1.4 MyAssertion.java


 実行時には、「-ea」または「-enableassertions」オプションを追加します。このオプションを追加しないと、実行時にアサーションは無視されます。実行時にオプションを追加した例を以下に示します。

java -ea MyAssertion


アサーションの例

 アサーションを用いて、DBCの事前条件・事後条件を指定するサンプルコードを以下に示します。以下のサンプルコードでは、プリペイドカードの残高を管理するクラスにおいて、カードの初期作成時と、カードで買い物をする際に正しい処理が行われているかどうかをアサーションでチェックしています。

PrepaidCard.java
public class PrepaidCard {
  public int amount;

  public PrepaidCard(int precharge) {
    assert precharge >= 0 : "初期残高が不正です";//事前条件

    amount = precharge;

    assert amount >= 0 : "残高が不正です";//事後条件
  }
  public void pay(int price) {
    assert price >= 0 : "価格が負になっています 価格:" + price; // 事前条件
    assert amount >= price : "価格が残高を超えています 価格:" + price + " 残高:" + amount; //事前条件

    amount -= price;
    System.out.println("価格:" + price +  " 残高:" + amount);
    
    assert amount >= 0 : "残高が不正です"; //事後条件
    
  }

  public static void main(String args[]) {
    PrepaidCard card = new PrepaidCard(1000);//1000円のプリペイドカードを作成
    card.pay(300);//300円の正常な買い物
    card.pay(500);//500円の正常な買い物
    card.pay(-200);//不正な買い物
  }
}


実行結果
価格:300 残高:700
価格:500 残高:200
Exception in thread "main" java.lang.AssertionError: 価格が負になっています 価格:-200
at PrepaidCard.pay(PrepaidCard.java:12)
at PrepaidCard.main(PrepaidCard.java:26)


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。