Javaの例外について

投稿日: 更新日:

以下の記事は書いたのがだいぶ古いので、後で整理します。。。

例外の種類

まず知っておくべきこととして、 Javaには、Error, Exception, RuntimeExceptionの3種類があります。 これは以下の階層になっています。

  • Throwable
    • Error
    • Exception(チェック例外)
      • RuntimeException(非チェック例外)

Errorの扱いについて

Errorとそのサブクラスのうちよく見るものを以下に掲載します。

  • Error
    • VirtualMachineError
      • StackOverflowError
      • InternalError
      • OutOfMemoryError

OutOfMemoryErrorなど、JVMが発生させるエラーが該当します。 自作のErrorを用意することはまずありません。

明示的にErrorを投げるケースは、言語仕様として発生しない場合がありえますが1、 分からない場合はIllegalStateExceptionを投げるので十分だと思います(どちらにしても発生しないので)。

チェック例外と非チェック例外の使い分け

Javaの特徴として、チェック例外は呼び出し元でcatchかthrowsで処理しなければならない、 非チェック例外は処理が不要という特徴があります。 したがって、通常起こりうる例外はチェック例外、通常起こりえない例外、 あるいは起きても対処不可能な例外は非チェック例外と使い分けるのが基本です。

ただし、「通常起こりうる例外」の解釈は、プログラムによって異なります。 例えば、データベース管理ソフトを開発している場合、 データベース接続エラーは通常起こりうる例外ですが、 業務システムの場合、データベース接続エラーは通常起こりえない例外とみなす方が自然です。

明らかに「通常起こりえない例外」として扱うべきものは、以下のようなものです。

  • プログラムのバグによるもの
    • IllegalArgumentException
    • IllegalStateException
  • 環境の不備によるもの
    • ClassNotFoundException
  • その他絶対呼び出されるはずがないもの
    • UnsupportedOperationException
  • ハードウェア障害によるもの

どのような例外を投げるべきか

自分は以下のように考えています。

  • 明らかに他の例外が望ましい場合はその例外を使用する。
    • メソッドに渡す引数が正しくない場合→IllegalArgumentException
    • クラスの内部状態が正しくない場合→IllegalStateException
    • そのメソッドがサポートされてない場合→UnsupportedOperationException
  • それ以外は原則として、独自のチェック例外を定義して投げる

ただし、独自例外の作成方法についてはあくまで指針であって、規則ではありません。

独自のチェック例外を投げるのが望ましい理由

以下の2つの理由から、独自例外を定義した方が良いです。 ただし、通常起こりえない例外に関しては、非チェック例外でも構いません。

例外固有のメソッドが定義可能

汎用例外を使用する場合、独自メソッドの定義が行えません。 最初は汎用例外でよくても、あとで例外の詳細情報が必要になることもあります。

ライブラリや特定の機能への依存を避けるため

例えば、javax.mail.MessagingExceptionを投げるメソッドがある場合、 そのメソッドはJavaMailに依存してしまいます。 外部ライブラリが提供する例外をそのまま使用する場合、そのライブラリに依存してしまいます。

チェック例外の方が望ましい理由

チェック例外にすることによって、呼び出し元がこのような例外処理が必要というのを意識させるためです。

あとで独自例外が必要になったとき

最初は汎用的な例外を使用していたが、独自例外を使用する必要が出てきた場合は、以下の回避方法があります。

独自例外を元の例外のサブクラスにする

例えば、元のメソッドがIOExceptionを使用していたが、独自例外を定義する必要が出てきた場合は、独自例外をIOExceptionを継承することで、呼び出し元のコードは修正なく動作します。もちろん、独自例外固有のメソッドを使用する場合は、修正が必要です。

public class MyIOException extends IOException {
    ...
}

public void foo() throws IOException {
    ...
}

try {
    foo();
} catch (IOException e) {
    ...
}

例外連鎖

例えば、SQLExceptionはチェック例外ですが、業務システムなど、基本的に非チェック例外として扱ってよい場合は、例外連鎖という仕組みを使用します。この仕組みを利用した例として、JDK 8から提供されている、UncheckedIOExceptionというクラスがあります。

public class UncheckedSQLException {
    SQLException sqle;
    public UncheckedSQLException(SQLException cause) {
        this.sqle = cause;
    }

    public SQLException getCause() {
        return this.sqle;
    }
}

辞書

  • Java
  • exception: まだありません。