Java例外備忘録、最後の記事になる予定だ。
今回は例外を自分で発生させたり、あるいは呼び出し元に例外が発生したことを通知するような書き方をまとめていく。
通知、という言い方が正しいか分からないが、イメージとしては通知と捉えている。
それともう一つ、自分で新しい例外を作ることもやってみる。
改めてになるが、本シリーズは備忘録だ。
勉強した内容をまとめているので、誤っている可能性もある。
その点、ご了承いただければ幸いだ。
というわけで、説明に入っていこう。
例外を自分で発生させる
まずはこちら。
ちょっと説明が難しいので、いきなりサンプルソースを出してしまおう。
private int div(int a, int b) {
int res = 0;
try{
if(b != 0) {
res = a / b;
} else {
throw new ArithmeticException();
}
}catch(ArithmeticException e){
System.out.println("0割りが発生したため、0を返します。 at div()");
return 0;
}
return res;
}
このサンプルは恐らく悪い書き方なのだが、そこは置いておいて…
注目して欲しいのは7行目。
ここで、throwという処理が入っている。
これが、例外を自分で意図的に発生させる書き方だ。
throw 例外クラスのインスタンス;
という書き方で、その例外を発生させることができる。
で、ここで発生したら前回と同様、catchしていればそこの処理へ移る。
要するに、前回は計算する際にゼロ割していたら発生していたものを、今回は自分から発生させていることになる。
なお、ここでもチェック・非チェック例外の決まりは同じだ。
例外クラスがチェック例外なら例外処理を書かなければいけないし、非チェック例外なら書かなくてもいい。
…が、この書き方で非チェック例外を出すことはほとんどないように思っている。
だから、上の例は良くないのだろうなぁと。
呼び出し元へ例外を通知する
こちらは、発生した例外をそこで処理するのではなく、呼び出し元に通知を行う書き方だ。
こちらも先にサンプルを載せてしまおう。
private int div(int a, int b) throws ArithmeticException{
int res = 0;
if(b != 0) {
res = a / b;
} else {
throw new ArithmeticException();
}
return res;
}
今度は関数の定義部分になんか追加されている。
これは、この処理によってその例外が発生する可能性があることを表している。
実際、6行目で例外を発生させている。
こうしたら、呼び出す側でその例外処理を書けるようになる。
これも例を非チェック例外で書いてしまっているが、基本的にはチェック例外で書くことになるだろう。
例外クラスを自分でつくる
ここからは、元々Javaに用意されていない例外を自分で作っていく。
で、これには元々用意されている例外クラスを継承するのだが、そこの構造のお話からしていこう。
例外クラス群の構造
前回、例外処理を書く時に、対処する例外クラスをcatchに書くと解説した。
そこに書く例外クラスには、継承関係があるということも書いたと思う。
だが、例外クラスの一番大元がExceptionというクラスということしか書いていなかった。
これを、詳細に見ていこう。
簡単に書いてしまうと、以下の図のような階層構造になっている。
上がスーパークラス、下がサブクラスの関係だ。
赤枠のところに、具体的なエラー、あるいは例外のクラスが入ってくる形になっている。
なお、公式ドキュメントでもこの階層構造は調べられるので、よかったら参考にしてほしい。
最上部にあたるThrowableクラスのドキュメントを貼っておこう。
Throwable
こいつが、例外とエラーの更に上位にある。
つまり、全ての例外やエラーは、このクラスを継承している、ということになる。
Errorクラス
これは、前々回使った用語でいう狭義のエラーを表すクラスだ。
これもJava側で用意されている、が今回はそれくらいで。
Exceptionクラス
これが、例外全体の元となるクラスだ。
全ての例外クラスは、このクラスを直接、あるいは間接に継承している。
で、これを継承していて、かつ後述するRuntimeExceptionクラスを継承していないものが、チェック例外だ。
つまり、それに対しては例外処理の記述が必須になる。
具体例を挙げると、FileNotFoundException、IOExceptionなど。
で、これを継承してクラスを作ると、それを新たにチェック例外として使うことができる。
RuntimeExceptionクラス
これは、例外の中でも非チェック例外の元となっているクラスになる。
復習で、こちらは例外処理の記述は不要だった。
例は、NullPointerException、ArrayIndexOutOfBoundExceptionなどなど。
上のサンプルで使用したArithmeticExceptionもその一つだ。
これを継承すると、非チェック例外として新たに例外クラスを作れる…のだが、こちらは使い道があるのだろうか?
要するに、自分で新しい例外クラスを作る場合は、基本的にチェックする前提だろう。
で、そのためにはExceptionクラスを継承すればいい、と認識している。
このあたりのベストプラクティスまでは調べていないので、そこが今後の課題だ。
実際に使ってみた
さて、では新たに例外クラスを作ってそれを発生させてみよう。
まずは、新しくMyTestExceptionという例外クラスを作る。
class MyTestException extends Exception {
}
今回はとりあえず使ってみたいだけなので、中身は一切定義しない。
で、これを使ってみよう。
private int div(int a, int b){
int res = 0;
try{
if(a == 0){
throw new MyTestException();
}
res = a / b;
}catch(MyTestException e){
System.out.println("0を割ろうとしているため、0を返します。 at div()");
return 0;
}catch(ArithmeticException e){
System.out.println("0割りが発生したため、0を返します。 at div()");
return 0;
}
return res;
}
使い方は、基本的に元々用意されているものと同じ。
throwしなければ発生しないので、throwして例外を発生させる。
で、catchする部分でも、その例外クラスで受け取る処理を書く。
これで、自作の例外クラスが使用できる。
今回のような短い例だとなかなか恩恵は受けづらいと思うが、長くなってくると効力を発揮する…と思う。
ちょっとまだ落とし込めていない部分もあるので、このくらいで。
まとめ:例外発生・通知(?)と自作例外クラス
まとめに入ろう。
まず、自分で例外を発生させる場合は、throw文を使用した。
throw 例外クラスのインスタンス;
で、その例外をそこで発生させることができる。
また、例外を呼び出し元に通知する場合は、関数宣言部にthrows 例外クラス名
と付け加えることで、通知することができる。
自分で例外クラスを作る場合は、チェック例外ならExceptionクラスを、非チェック例外ならRuntimeExceptionクラスを継承する。
…が、恐らくチェック例外を作ることになるだろう。
かなり苦手意識があった例外周りだが、改めて勉強してみて、ある程度は整理できてきた。
ただ、使わなければこれ以上理解を深めるのは難しそうだ。
もしかしたら、今後使ってみた結果を再度まとめる…かもしれない。
というわけで、これでJava例外備忘録編は終わりにしよう。
それでは。
コメント