1. 概要

この記事では、 Exception とは何かを簡単に説明し、Javaでの連鎖例外について詳しく説明します。

簡単に言えば、例外は、プログラムの実行の通常の流れを妨げるイベントです。 ここで、例外を連鎖させて、例外からより良いセマンティクスを引き出す方法を正確に見てみましょう。

2. 連鎖例外

連鎖例外は、ある例外がアプリケーションで別の例外を引き起こす状況を識別するのに役立ちます。

たとえば、ゼロで除算しようとしたためにArithmeticException をスローするメソッドを考えてみますが、例外の実際の原因は、除数をゼロにするI/Oエラーでした。メソッドは[をスローします。 X230X]ArithmeticExceptionを呼び出し元に送信します。 発信者は、例外の実際の原因を知りません。 このような状況では、連鎖例外が使用されます。

この概念は、JDK1.4で導入されました。

Javaで連鎖例外がどのようにサポートされているかを見てみましょう。

3. スロー可能クラス

Throwable クラスには、連鎖例外をサポートするためのコンストラクターとメソッドがいくつかあります。 まず、コンストラクターを見てみましょう。

  • Throwable(Throwable cause) Throwable には、Exceptionの実際の原因を指定する単一のパラメーターがあります。
  • Throwable(String desc、Throwable cause) このコンストラクターは、Exceptionの実際の原因とともにException記述を受け入れます。

次に、このクラスが提供するメソッドを見てみましょう。

  • getCause()メソッド –このメソッドは、現在のExceptionに関連付けられている実際の原因を返します。
  • initCause()メソッドExceptionを呼び出すことで根本的な原因を設定します。

4. 例

次に、独自の Exception の説明を設定し、連鎖したExceptionをスローする例を見てみましょう。

public class MyChainedException {

    public void main(String[] args) {
        try {
            throw new ArithmeticException("Top Level Exception.")
              .initCause(new IOException("IO cause."));
        } catch(ArithmeticException ae) {
            System.out.println("Caught : " + ae);
            System.out.println("Actual cause: "+ ae.getCause());
        }
    }    
}

推測されるように、これは次のことにつながります。

Caught: java.lang.ArithmeticException: Top Level Exception.
Actual cause: java.io.IOException: IO cause.

5. なぜ連鎖例外なのか?

ログを読み取り可能にするには、例外を連鎖させる必要があります。 2つの例を書いてみましょう。 1つ目は例外を連鎖させず、2つ目は例外を連鎖させます。 後で、両方の場合でログがどのように動作するかを比較します。

まず、一連の例外を作成します。

class NoLeaveGrantedException extends Exception {

    public NoLeaveGrantedException(String message, Throwable cause) {
        super(message, cause);
    }

    public NoLeaveGrantedException(String message) {
        super(message);
    }
}

class TeamLeadUpsetException extends Exception {
    // Both Constructors
}

それでは、コード例で上記の例外の使用を開始しましょう。

5.1. 連鎖なし

カスタム例外を連鎖させずにサンプルプログラムを書いてみましょう。

public class MainClass {

    public void main(String[] args) throws Exception {
        getLeave();
    }

    void getLeave() throws NoLeaveGrantedException {
        try {
            howIsTeamLead();
        } catch (TeamLeadUpsetException e) {
            e.printStackTrace();
            throw new NoLeaveGrantedException("Leave not sanctioned.");
        }
    }

    void howIsTeamLead() throws TeamLeadUpsetException {
        throw new TeamLeadUpsetException("Team Lead Upset");
    }
}

上記の例では、ログは次のようになります。

com.baeldung.chainedexception.exceptions.TeamLeadUpsetException: 
  Team lead Upset
    at com.baeldung.chainedexception.exceptions.MainClass
      .howIsTeamLead(MainClass.java:46)
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:34)
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29)
Exception in thread "main" com.baeldung.chainedexception.exceptions.
  NoLeaveGrantedException: Leave not sanctioned.
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:37)
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29)

5.2. 連鎖あり

次に、カスタム例外を連鎖させた例を書いてみましょう。

public class MainClass {
    public void main(String[] args) throws Exception {
        getLeave();
    }

    public getLeave() throws NoLeaveGrantedException {
        try {
            howIsTeamLead();
        } catch (TeamLeadUpsetException e) {
             throw new NoLeaveGrantedException("Leave not sanctioned.", e);
        }
    }

    public void howIsTeamLead() throws TeamLeadUpsetException {
        throw new TeamLeadUpsetException("Team lead Upset.");
    }
}

最後に、連鎖例外で取得されたログを見てみましょう。

Exception in thread "main" com.baeldung.chainedexception.exceptions
  .NoLeaveGrantedException: Leave not sanctioned. 
    at com.baeldung.chainedexception.exceptions.MainClass
      .getLeave(MainClass.java:36) 
    at com.baeldung.chainedexception.exceptions.MainClass
      .main(MainClass.java:29) 
Caused by: com.baeldung.chainedexception.exceptions
  .TeamLeadUpsetException: Team lead Upset.
    at com.baeldung.chainedexception.exceptions.MainClass
  .howIsTeamLead(MainClass.java:44) 
    at com.baeldung.chainedexception.exceptions.MainClass
  .getLeave(MainClass.java:34) 
    ... 1 more

表示されたログを簡単に比較して、連鎖した例外がよりクリーンなログにつながると結論付けることができます。

6. 結論

この記事では、連鎖例外の概念について説明しました。

すべての例の実装は、 Githubプロジェクトにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。