Java言語の例外処理
Java言語には「例外処理」と呼ばれる機能があります。
(実は、この機能の利用法は C++ にある例外処理とほぼ同じです。
しかし、ここではその機能を使ったことがない人のために、
あらためて説明します。)
では、Java言語で例外処理をどんなふうに使えばいいかを、
具体例を通じて説明しましょう。
例外処理を扱うキーとなる予約語は次の3つです。
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Color;
/** Exception の機能を実験するための ExcepTestクラス */
public class ExcepTest extends Applet {
/** 描きたい円の半径 */
protected int radius=0;
/** HTMLからパラメータを受け取り、Strig型からint型に変換する */
public int getIntValue( String keyword ) throws NoValueException {
String valueString = getParameter( keyword );
if( valueString == null )
throw new NoValueException(); // 値が不明という例外
Integer I = new Integer( valueString );
return I.intValue();
}
/** 初期設定の関数。*/
public void init() {
try {
radius = getIntValue("radius"); // チェックの対象
}
catch( NoValueException e ) {
radius = size().height/2; // デフォルト値を与える
}
if( radius <= 0 )
radius = size().height/2; // デフォルト値を与える
}
/** アプレットの表示を行う関数。*/
public void paint( Graphics g ) {
int x = size().width/2 - radius;
int y = size().height/2 - radius;
g.setColor( Color.yellow );
g.fillArc( x, y, radius*2, radius*2, 0, 360 );
}
}
/** 該当する数値データがないという例外クラス */
class NoValueException extends Exception {
NoValueException() {
super(); // このクラスは特別な機能は追加していない
}
}
解説しましょう。
例外を throw している関数は getIntValue() という名前です。
この関数はパラメータの値を HTML のページから受け取り、
それを String型から int型のデータに変換して返します。
上のクラスでは、描きたい円の半径をパラメータとして
受け取ろうとしています。
HTML の側でパラメータの値を指定しない場合もあり得ます。
その場合 getIntValue() は呼び出し側にエラーを伝えなければなりません。
今の例では、返す値は円の半径ですから、
0 もしくは -1 をエラーの意味で返す方法が可能です。
しかし getIntValue() は便利な関数で、
別のパラメータを受け取る目的で繰り返し使いそうです。
その場合には、0 や -1 を目的の数値として返す必要が出てくるかも
しれません。
そこで上の例のように、
「値が見つからない」という意味の例外 NoValueException を
throw する設計になったわけです。
さて、例外そのものがクラスとして定義されているのに驚いた人も いるでしょう。(Java言語は何をするのにもクラスを使います。) 例外のクラスは、すべてクラス Exception のサブ・クラスとして 定義されます。 上の例は単純にするため新しいデータを追加するようなことはしていません。 実際には、それぞれの例外クラスごとに処理に必要なデータ (たとえばエラーメッセージの文字列など)を追加することができます。 また、throw される時に NoValueException のオブジェクト が新たに生成されていることにも注目してください。 これは動的に変化するデータを、エラー発生時の状況ごとに 独立した情報として伝えることを保証します。
次に例外を受け取る側を見てみましょう。
関数 getIntValue() は、関数 init() の中で呼び出されます。
この呼び出しを tryブロックの中に置いています。
それに続く catchブロックで
発生が予測される NoValueException に対する処理を用意して
おきます。もちろん例外が発生しなければ、この中の処理は無視されます。
例外が複数種類予想される場合は、複数の catchブロックを
置くことができます。ちょうど switch-case の構文のようになりますね。
tryブロックの中に複数の行を書いて、
同じ種類の例外を一括して処理させることも可能です。
(ただし、上手にまとめないとかえって混乱します。)
さて注意深い人は、init() の中で getIntValue() の返値を
改めてチェック(正の値かどうか)しているのをおかしいと感じたかもしれません。
なぜ例外処理にしなかったのでしょうか?
これにはちゃんとした理由があります。(手を抜いたわけではありません。)
getIntValue() は正負にかかわらず常に値を返せるように設計したからです。
別の場所で getIntValue() を使う時には負の値をエラーではなく
受け取るケースも考慮したのです。
もし正の値以外を例外処理でチェックしたいのなら、
正の整数値を専用に受け取るための、別の新しい関数を定義するべきでしょう。
その新しい関数は NoValueExceptionの他に、
値が正でない場合、別の種類の例外を throw するようにします。
呼び出す側では複数のcatchブロックが必要となります。
実用的かどうかはわかりませんが、
ちょうど良い練習問題なので、
興味のある人は試してみてください。