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
のみをキャッチすることが許可された場合、何が問題になる可能性がありますか?例は大歓迎です。
Java 7が導入されました より包括的な例外型チェック 。
ただし、Java SE 7では、rethrowExceptionメソッド宣言のthrows句で例外タイプFirstExceptionおよびSecondExceptionを指定できます。Java SE 7コンパイラは、ステートメントthrow eによってスローされた例外はtryブロックからのものである必要があり、tryブロックによってスローされた例外はFirstExceptionおよびSecondExceptionのみであると判断します。
この一節は、try
およびFirstException
を具体的にスローするSecondException
ブロックについて話しています。 catch
ブロックはException
をスローしますが、メソッドが宣言する必要があるのは、FirstException
とSecondException
をスローし、Exception
はスローしないことです。
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }
これは、test
でスローされる唯一の可能な例外タイプがError
sまたはRuntimeException
sであり、どちらもキャッチする必要がないことをコンパイラーが検出できることを意味します。 throw e;
、静的型がException
の場合でも、宣言または再キャッチする必要がないことを通知できます。
しかし、それをException
にキャストすると、そのロジックはバイパスされます。現在、コンパイラはそれを通常のException
として扱います。これは、キャッチまたは宣言する必要があります。
このロジックをコンパイラに追加する主な理由は、特定のサブタイプをキャッチする一般的なthrows
を再スローするときに、プログラマがException
句で特定のサブタイプのみを指定できるようにすることでした。ただし、この場合は、一般的なException
をキャッチでき、throws
句。スローできる特定のタイプは、チェックされた例外ではないためです。
ここでの問題は、チェックされた/チェックされていない例外の制限が、コードに許可されているthrowではなく、許可されているcatchに影響することです。あなたはまだcatchどのタイプのException
でも可能ですが、実際に再びスローできるのはチェックされていないものだけです。 (これが、チェックされていない例外をチェックされた例外にキャストすると、コードが壊れる理由です。)
未チェックの例外(別名Exception
s)はExceptionのサブクラスであり、標準のポリモーフィズムルールに従うため、RuntimeException
で未チェックの例外をキャッチすることは有効です。 Exception
にString
を格納してもObject
intoString
にならないのと同じように、キャッチされた例外を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_error
、std::logic_error
、std::bad_alloc
、適切に定義されたユーザー作成の例外などもキャッチされます。 std::exception
から派生。
tl; dr:checked例外をキャッチしていません。any例外をキャッチしています。例外は、チェック例外タイプにキャストした場合にのみチェック例外になります。