- Java における例外は実行中のメソッドの失敗を呼び出し元に通知するオブジェクト
- すべての例外は
Exception
クラスの直接または間接のサブクラスのインスタンス - オブジェクトにする以外にも以下の方法があるが、オブジェクトが拡張性とシンプルさでよさそう
int
値を用いる方法- メリット
- シンプル
- C 言語などの API と同様な扱い方ができる
- デメリット
- 関数・ライブラリーごとにエラー値の体系が異なりミスを多発しやすい
- 詳細なエラーの情報を添付できない
- メリット
String
を用いる- メリット
- 例外の表現として柔軟・シンプル
- 詳細情報が付与できる
- デメリット
- 例外ごとに情報を取り出すためのパーサーが必要になる
- メリット
- 独自の例外を作ると、きめ細かい例外処理が行える
Exception
クラスあるいはRuntimeException
クラスを継承したクラスを作る
class NoBeerException extends Exception {
NoBeerException(String message) {
super(message);
}
}
- 独自例外クラスを用いると
Exception
クラスのインスタンスだけでは実現できない、豊富な情報を伝えられる
class ItemNotFoundException extends Exception {
final int itemId;
final int categoryId;
ItemNotFoundException(int itemId, int categoryId) {
super(
"item not found, id: %d, category: %d"
.format(itemId, categoryId));
this.itemId = itemId;
this.categoryId = categoryId;
}
}
- 呼び出し元に例外処理を強制する場合は
Exception
クラスのサブクラスを作る- ただし
RuntimeException
のサブクラスにしない
- ただし
- 呼び出し元に例外処理を強制しない場合は
RuntimeException
クラスのサブクラスを作る
Exception
クラスのコンストラクターには以下のものがあり、例外の原因を伝えられる
Exception(String message)
Exception(Exception cause)
Exception(String message, Exception cause)
Exception
クラスから情報を取得する方法
getMessage()
メソッドによって、例外に設定されたメッセージを取得できるgetCause()
メソッドによって、例外の原因の例外を取得できる
Exception outOfGas = new NoBeerException("out of gas");
Exception exception = new NoBeerException("Beer has gone", outOfGas);
exception.getMessage(); // Beer has gone
exception.getCause(); // -> NoBeerException : out of gas
Exception
クラスには、呼び出しの階層を記録する機能がついているgetStackTrace()
メソッドにより、呼び出しの階層の記録StackTraceElement
の配列を取得できるStackTraceElement
には次の情報が含まれている- クラス名
- メソッド名
- ファイル名
- 行番号
java.lang.NullPointerException
at com.example.Library.nameOf(Library.java:22)
at com.example.UserCode$LibraryExecutor.execute(UserCode.java:49)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Me
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMeth
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Delega
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at com.example.UserCode.reflectiveInvoke(UserCode.java:63)
at com.example.UserCode.main(UserCode.java:31)
このスタックトレースからは...
Library.java
というファイルの22
行目Library
というクラスにあるnameOf
というメソッドの中でNullPointerException
が発生した
return
の代わりにthrow
キーワードに例外オブジェクトを指定することでメソッドの実行を終了し、失敗したと呼び出し元に通知できる
if (beer.isPresent()) return Pair.of(beer.get(), change);
// メソッドの実行を終了して失敗を通知
throw new NoBeerException("beer has gone");
- なお、通知する例外が
Exception
クラスのサブクラスで、RuntimeException
のサブクラスではない場合、 メソッドのシグニチャー(メソッドの型情報)にthrows
キーワードと通知する例外クラスを記述する必要がある
public Pair<Beer, Change> pouringBeer()
throws NoBeerException, NoChangeException {
...
if (beer.isPresent()) return Pair.of(beer.get(), change);
// メソッドの実行を終了して失敗を通知
throw new NoBeerException("beer has gone");
}
- 例外が発生しうる箇所を
try {}
ブロックで囲む - 処理対象の例外型を
catch()
で指定し、例外パラメーターを受け取る - 続くブロックに例外処理を記述する
try {
Beer beer = beerServer.pourBeer(1);
Change change = changeReserve.prepareChange(payment);
return Pair.of(beer, change);
} catch (NoBeerException e) {
// ビールがない場合の例外処理を記述する
} catch (NoChangeException e) {
// お釣りがない場合の例外処理を記述する
}
catch()
でのマッチングは上から順番に行われるException
やRuntimeException
などのルートになる例外を上の方に指定すると、処理できないルートが発生し、コンパイルエラーになる
try {
Beer beer = beerServer.pourBeer(1);
Change change = changeReserve.prepareChange(payment);
return Pair.of(beer, change);
} catch (Exception e) {
...
} catch (NoBeerException e) { // !!コンパイルエラーになる!!
...
}
finally {}
ブロックに処理を記述するAutoCloseable
の実装クラスを作り、close()
メソッドに処理を記述した上で、try() {}
のカッコ内に指定する
BeerValve valve = ...;
try {
valve.open();
MugOfBeer mugOfBeer = pourBeer(mugCup);
return mugOfBeer;
} finally {
valve.close();
}
BeerValve valve = ...;
AutoCloseable closingValve = new AutoCloseable() {
@Override public void close() throws Exception {
valve.close();
}
};
try(closingValve) {
valve.open();
MugOfBeer mugOfBeer = pourBeer(mugCup);
return mugOfBeer;
}
AutoCloseable
は複数個指定できる
BeerValve valve = ...;
AutoCloseable closingValve = valve::close;
try(closingValve; MugHolder mugHolder = openMugHolder()) {
MugCup mugCup = mugHolder.cupAtHead();
valve.open();
MugOfBeer mugOfBeer = pourBeer(mugCup);
return mugOfBeer;
} // mugHolder -> closingValve の順に実行される
// try() の中にて取得したオブジェクトのスコープは try {} ブロック内