|
Assertion Facility |
|||
Assertion ってなに? |
|||
アプリケーションを作っているときに、特にデバッグを行っているとき、いろいろとデバッグのためにプリント文を入れることがありますね。例えば、次のような時です。
赤で示した部分はデバッグのために使用しています。本来なら、x は負の数 にならないはずなんだけど、どこかが間違っているので負になっちゃうことを教えてくれるわけですね。 個人的に使っているアプリケーションなら、このようなデバッグのためのコードを入れたままにしておいてもいいかもしれませんが、できればアプリケーションをリリースするときには必要がないコードになります。 こんなときに Assertion が使用できます。 つまり、Assertion はデバッグ版の時だけ、式を評価するための機構になります。C/C++ でも同様の機構があります。こちらは NDEBUG がマクロ定義されているかどうかで、実行されたりされなかったりします。 なんだこれだけかと思わないでください。Assertion は API ではなく、言語自体の文法の拡張になるのです。文法の変更という点では Assertion と Java 2 SE, v1.5 で導入が予定されている Generics が最後になると James Gosling 氏が JavaOne 2001 の Keynote Speech で述べています。 せっかくの機能なので、有効にデバッグにいかしましょう。 ところで、Assertion 自体は API ではないので、今回はサンプルプログラムは特にありません。ほとんどプログラムの断片みたいなものですが、御了承ください。
|
どうやって書くか | |||||||||
書き方はとても簡単で、単に次のように書けばいいだけです。
式1 には boolean の値になるようにします。また、式 2 はプリミティブもしくは文字列にします。 例えば上記の例だと
と書くことができます。assert 文の式1 の値が false の時に、AssertionError が発生します。 式2 が入った場合だと
とか
のように記述できます。 式2 がある場合は、AssertionError と一緒に式2 が出力されます。 これからの例で使うためのクラスをここで作っておきましょう。クラス名は AssertionTest にしました。
いちおう、ダウンロードもできるようにしておきます。
|
次はコンパイル |
||||
Assertion を入れるために Java2 SDK, v1.4 からクラスファイルの構造が変更されました。v1.4 形式のクラスファイルを作るためには、-source オプションを使用します。-souce オプションには v1.4 の場合は 1.4、v1.3.x の場合は 1.3 を指定します。 例えば、assert 文を含んだ AssertionTest.java をコンパイルする場合は次のように行います。
-souce オプションのデフォルトは 1.3 なので、-source オプションを指定しないと次のようにコンパイルエラーが出てしまいます。
|
Assertion の使い方 |
|||||||||||||||||||||||||||||||||||||||||||||||
コンパイルができたので、さっそく実行してみましょう。 単に実行すると、assert 文は無視されてしまいます。Assertion を有効にするには次のように実行します。 java [ -enableassertion | -ea ] [ :<package name>"..." | :<class name> ] と Assertion のドキュメントには書いてあるのですが、分かりにくいですね。-ea もしくは -enableassertion というオプションを使用することで Assertion が有効になることは分かりますが... 同じように Assertion を無効にするには次のように実行します。 java [ -disableassertion | -da ] [ :<package name>"..." | :<class name> ] 両方とも分かりにくいので、いろいろなパターンについてまとめたものを次表に示しました。
-da オプションは単独で使うことはほとんどなく、-ea オプションと組み合わせて使うのだと思います。 それではさっそく AssertionTest を実行してみましょう。 まずはオプションを指定しないで、Assertion が無効になることを確認。
次に -ea オプションを使用してみます。
こんなふうに、assert 文が実行されて AssertionError が発生します。次に引数を 0 にしてみましょう。
assert 文の式の評価が true であれば AssertError は発生しません。AssertionTest.java では
のように value が 0 であれば true になるので、AssertionError が発生しなかったのです。 ところで、assert 文の書き方は次のようになりますが、式2 がないときを試してみましょう。
式2 がない形に AssertionTest.java を変更して実行してみました。
実行結果はこうなります。違いが分かりますか?
2 行目の AssertionError の後に value の値が出力されていないことが異なる部分です。式 2 の内容が出力されると書きましたが、実際にやってみないとよく分からないですね。
|
どこでどうやって使うか |
||||||
それが一番の問題です ^^;; 簡単に想像がつくのは if 文や switch 文などの分岐に使用する場合です。
assert false; と書いてしまえば、assert 文が実行されるときには常に AssertionError が発生します。 同じように switch 文でも使えます。たとえば、すべての分岐を網羅した case 文が書いておいて、default に assert 文を入れておきます。本来ならどこかの case 文に相当するのでしょうが、何らかの間違ったデータが入っていたときに default に飛んで AssertionError が発生されるということです。
もちろん、関数などでも使えます。例えば、foo というメソッドがコールされるときには、プロパティ x が null ではいけないという条件があるとします。そんなときには
とでもしておけば、null だった場合には AssertionError が発生します。 Assertion がないときに、筆者はデバッグのためにこんなコードを書くことがありました。
別に Exception をスローするわけではないのですが、こうすると foo がコールされるとスタックトレースが出力されるので、どこから呼ばれているかを確かめることができるのです。もちろん、リリース時にはこのコードは抜いてしまいます。 でも、Assertion があれば、これの代わりにすることができますし、リリース版でも assert 文を削除する必要はありません。 Effel というオブジェクト指向言語を開発した Meyer さんによれば、アサーションは事前条件、事後条件、invaliant からなるそうです。事前条件はある処理を行う前に成立していなければならない条件、事後条件は処理を行った後に成立していなければならない条件、invaliant は常に成立していなければならない条件です。 先ほどの、if 文や switch 文で Assertion を使用した例は invaliant に相当します。また、foo メソッドの例は事前条件です。 ここまでは、Assertion をデバッグに使うということで説明を加えてきましたが、同じく Meyer さんによれば Assertion には次のような用途が間がられると言っています。
1 は事前条件や事後条件などをきちんと記述しておくことで、アプリケーションの動作を正しく定義しておくことができることからきています。2 も 1 と同様にアプリケーションの動作を定義できるので、これをドキュメント代わりに使えるということです。 4 のフォルトトレラントは障害耐久性を高めるためです。アプリケーションには通常の例外処理では拾えないエラーってありますよね。例えば、変数 x は 0 以上でなければ正しく処理できないけど、負の値でもとりあえず処理はできてしまう (もちろん結果は間違っているんですが) なんてことが。 こんなときに Assertion を使って、AssertionError の例外処理でエラーの対処をすることもできます。 こんなふうにいろいろな場所で Assertion は使用することができます。アプリケーションを効率よく開発するために、うまく Assertion を使ってみてください。 ただし、容易に想像できるのですが、Assertion の使いすぎはパフォーマンスを落とします。-ea オプションが使用されていないときでも、assert 文を解釈するために時間がかかるためある程度はパフォーマンス低下は避けられません。まぁ、たいていはこのくらいのパフォーマンスダウンは問題にならないと思いますが... 適度に使って、効率よくアプリケーションの開発をしましょう。 参考 URL
(Jul. 2001) |
|