web-dev-qa-db-ja.com

例外をスローしないコードでチェック例外のキャッチが許可されているのはなぜですか?

Javaでは、checked例外( Exception またはそのサブタイプ-IOException、InterruptedExceptionなど)をスローするメソッドは、throwsステートメント:

public abstract int read() throws IOException;

throwsステートメントを宣言しないメソッドは、チェック済み例外をスローできません

public int read() { // does not compile
    throw new IOException();
}
// Error: unreported exception Java.io.IOException; must be caught or declared to be thrown

しかし、安全なメソッドでチェックされた例外をキャッチすることは、Javaでは依然として合法です。

public void safeMethod() { System.out.println("I'm safe"); }

public void test() { // method guarantees not to throw checked exceptions
    try {
        safeMethod();
    } catch (Exception e) { // catching checked exception Java.lang.Exception
        throw e; // so I can throw... a checked Exception?
    }
}

実は違う。それは少しおかしいです:コンパイラはeがチェック済み例外ではないことを知っており、それを再スローすることを許可します。物事は少しばかげています、このコードはコンパイルされません:

public void test() { // guarantees not to throw checked exceptions
    try {
        safeMethod();
    } catch (Exception e) {        
        throw (Exception) e; // seriously?
    }
}
// Error: unreported exception Java.lang.Exception; must be caught or declared to be thrown

最初のスニペットは、質問の動機でした。

コンパイラーは、チェックされた例外を安全なメソッド内でスローできないことを知っています。つまり、チェックされていない例外のみをキャッチできるようにするべきでしょうか。


主な質問に戻ります-この方法でチェック例外のキャッチを実装する理由はありますか?それは単なる設計上の欠陥ですか、それともいくつかの重要な要素が欠けていますか?おそらく下位互換性がないのでしょうか?このシナリオでRuntimeExceptionのみをキャッチすることが許可された場合、何が問題になる可能性がありますか?例は大歓迎です。

34
AdamSkywalker

Java 7が導入されました より包括的な例外型チェック

ただし、Java SE 7では、rethrowExceptionメソッド宣言のthrows句で例外タイプFirstExceptionおよびSecondExceptionを指定できます。Java SE 7コンパイラは、ステートメントthrow eによってスローされた例外はtryブロックからのものである必要があり、tryブロックによってスローされた例外はFirstExceptionおよびSecondExceptionのみであると判断します。

この一節は、tryおよびFirstExceptionを具体的にスローするSecondExceptionブロックについて話しています。 catchブロックはExceptionをスローしますが、メソッドが宣言する必要があるのは、FirstExceptionSecondExceptionをスローし、Exceptionはスローしないことです。

public void rethrowException(String exceptionName)
 throws FirstException, SecondException {
   try {
     // ...
   }
   catch (Exception e) {
     throw e;
   }
 }

これは、testでスローされる唯一の可能な例外タイプがErrorsまたはRuntimeExceptionsであり、どちらもキャッチする必要がないことをコンパイラーが検出できることを意味します。 throw e;、静的型がExceptionの場合でも、宣言または再キャッチする必要がないことを通知できます。

しかし、それをExceptionキャストすると、そのロジックはバイパスされます。現在、コンパイラはそれを通常のExceptionとして扱います。これは、キャッチまたは宣言する必要があります。

このロジックをコンパイラに追加する主な理由は、特定のサブタイプをキャッチする一般的なthrowsを再スローするときに、プログラマがException句で特定のサブタイプのみを指定できるようにすることでした。ただし、この場合は、一般的なExceptionをキャッチでき、throws句。スローできる特定のタイプは、チェックされた例外ではないためです。

11
rgettman

ここでの問題は、チェックされた/チェックされていない例外の制限が、コードに許可されているthrowではなく、許可されているcatchに影響することです。あなたはまだcatchどのタイプのExceptionでも可能ですが、実際に再びスローできるのはチェックされていないものだけです。 (これが、チェックされていない例外をチェックされた例外にキャストすると、コードが壊れる理由です。)

未チェックの例外(別名Exceptions)はExceptionのサブクラスであり、標準のポリモーフィズムルールに従うため、RuntimeExceptionで未チェックの例外をキャッチすることは有効です。 ExceptionStringを格納してもObjectintoStringにならないのと同じように、キャッチされた例外をObjectに変換しません。多態性とは、Objectを保持できる変数が、Objectから派生したもの(Stringなど)を保持できることを意味します。同様に、Exceptionはすべての例外型のスーパークラスであるため、Exception型の変数は、オブジェクトinto __ Exceptionを変更せずに、Exceptionから派生したクラスを保持できます。このことを考慮:

import Java.lang.*;
// ...
public String iReturnAString() { return "Consider this!"; }
// ...
Object o = iReturnAString();

変数の型がObjectであるにもかかわらず、oには引き続きStringが格納されますか?同様に、あなたのコードで:

try {
    safeMethod();
} catch (Exception e) { // catching checked exception
    throw e; // so I can throw... a checked Exception?
}

これが意味することは実際には "クラスExceptionと互換性のあるもの(つまり、Exceptionおよびそれから派生したもの)をすべてキャッチすることです。"同様のロジックが他の言語でも使用されています。たとえば、C++では、std::exceptionをキャッチすると、std::runtime_errorstd::logic_errorstd::bad_alloc、適切に定義されたユーザー作成の例外などもキャッチされます。 std::exceptionから派生。

tl; dr:checked例外をキャッチしていません。any例外をキャッチしています。例外は、チェック例外タイプにキャストした場合にのみチェック例外になります。