Java言語の例外処理

Java言語の「例外処理」について説明します。


Java言語には「例外処理」と呼ばれる機能があります。 (実は、この機能の利用法は C++ にある例外処理とほぼ同じです。 しかし、ここではその機能を使ったことがない人のために、 あらためて説明します。)
例外処理は簡単に言ってしまえば、実行時のエラー処理を行うための仕組みです。 では 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ブロックが必要となります。 実用的かどうかはわかりませんが、 ちょうど良い練習問題なので、 興味のある人は試してみてください。