5

Exception.fillInStackTraceは、Exceptionオブジェクトまたは派生Exceptionオブジェクトを返す必要があると思います。以下の2つの機能を考慮すると、

public static void f() throws Throwable {
    try {
        throw new Throwable();
    } catch (Exception e) {
        System.out.println("catch exception e");
        e.printStackTrace();
    } 
}
public static void g() throws Throwable {
    try {
        try {
            throw new Exception("exception");
        } catch (Exception e) {
            System.out.println("inner exception handler");
            throw e.fillInStackTrace();
        }
    } catch (Exception e) {
        System.out.println("outer exception handler");
        e.printStackTrace();
    }
}
  1. は最初の関数でをexception handlerキャッチできませんでした。new Throwable()f()
  2. は、2番目の関数でexception handlerをキャッチできます。e.fillInstackTrace()g()
  3. しかし、2番目の関数g()はまだする必要がありthrows Throwableます。捕まえることができたので、これは本当に奇妙e.fillInstackTrace()です。

だから私の質問は、Exception.fillInStackTraceがそのような奇妙な構文を開発する代わりに例外または例外派生を返さないのはなぜですか?

編集:私の質問
明確にするために:私が「奇妙な構文」とはどういう意味ですか

  1. Exception.fillInStackTrace()参照を返すためThrowable、参照を受け取る例外ハンドラーは例外Exceptionをキャッチできないはずです。javaは暗黙的なダウンキャストを許可しないため、のようなものにする必要がありますreturn (Exception)e.fillInstackTrace()
  2. Exception参照を受け取る例外ハンドラーが例外を処理できるように設計されているため、メソッドが例外をスローThrowableすることをマークする必要はありませんが、Javaコンパイラーはそうするように強制します。g()Throwable

ありがとう。

4

6 に答える 6

12

あなたの質問にはかなり困惑しています。あなたが理解していないJava例外/例外処理について明らかに何かがあります。それでは、最初から始めましょう。

Java では、すべての例外 (この用語が Java 言語仕様で使用されているという意味で) は、 のサブクラスである何らかのクラスのインスタンスですjava.lang.Throwable。Throwable の直接のサブクラスは 2 つ (2 つだけ) あります。すなわちjava.lang.Exceptionjava.lang.Error. これらすべてのクラスのインスタンス ... Throwable および Error のインスタンスを含む ... は、JLS では例外と呼ばれます。

例外ハンドラーは、catch宣言で使用される例外タイプと互換性のある割り当てである (JLS の意味での) 例外をキャッチします。たとえば、次のようになります。

try {
    ....
} catch (Exception ex) {
    ...
}

tryのインスタンスであるjava.lang.Exceptionか、直接または間接のサブタイプであるブロックでスローされた例外をキャッチしますjava.lang.Exceptionjava.lang.Throwableしかし、それは(明らかに)上記のいずれでもないため、 のインスタンスをキャッチしません。

一方で:

try {
    ....
} catch (Throwable ex) {
    ...
}

のインスタンスをキャッチしますjava.lang.Throwable

これに照らして例を確認すると、fメソッドが Throwable インスタンスをキャッチしていない理由は明らかです。catch 句の例外タイプと一致しません! 対照的に、このgメソッドでは、Exception インスタンスが catch 句の例外タイプと一致したため、キャッチされます。

Throwable を にスローする必要があることについて、あなたが何を言っているのかわかりませんg。まず、メソッドが Throwable をスローすると宣言しているという事実は、実際にそれをスローする必要があるという意味ではありません。それが言っているのは、Throwable に割り当て可能な何かをスローする可能性があるということだけです...おそらくメソッドの将来のバージョンgthrow e;次に、外側の catch ブロックに追加すると、 Throwable に割り当て可能なものをスローすることになります

最後に、一般的に、Throwable、Exception、Error、および RuntimeException のインスタンスを作成することはお勧めできません。そして、いつ、どのように捕まえるかには細心の注意を払う必要があります。例えば:

try {
     // throws an IOException if file is missing
    InputStream is = new FileInputStream("someFile.txt");
    // do other stuff
} catch (Exception ex) {
    System.err.println("File not found");
    // WRONG!!!  We might have caught some completely unrelated exception;
    // e.g. a NullPointerException, StackOverflowError, 
}

編集- OPのコメントに応じて:

しかし、 throw e.fillInStackTrace(); でスローするもの 例外ではなく、Throwable のインスタンスである必要があります。

Javadoc は、返されたオブジェクトが、メソッドを呼び出している例外オブジェクトであることを明確に示しています。このメソッドの目的はfillInStacktrace()、既存のオブジェクトのスタック トレースを埋めることです。別の例外が必要な場合は、 を使用newして作成する必要があります。

実際には、外側の例外ハンドラは、throw e.fillInStackTrace() によってスローされた Throwable をキャッチしてはならないということです。

その理由を説明しました-Throwableは実際には元の例外であるためです。私の説明について、あなたが理解できない何かがありますか、それとも単に Java の定義方法が気に入らないと言っているのですか?

編集2

また、外部例外ハンドラーが Throwable 例外を処理できる場合、メソッド g が Throwable 例外をスローするように指定する必要があるのはなぜですか

あなたは私が言ったことを誤解しています...それは、例外をスローした場合、throws Throwable冗長ではないということでした。OTOHさん、ようやくあなたの不満を理解できたと思います。

あなたの苦情の核心は、これでコンパイルエラーが発生することだと思います:

public void function() throws Exception {
    try {
        throw new Exception();
    } catch (Exception ex) {
        throw ex.fillInStackTrace();
        // according to the static type checker, the above throws a Throwable
        // which has to be caught, or declared as thrown.  But we "know" that the 
        // exception cannot be anything other than an Exception.
    }
}

これはやや予想外であることがわかります。しかし、それは避けられないのではないかと心配しています。fillInStacktraceすべての場合に機能する の署名を宣言できる方法はありません (Java の型システムへの大幅な変更を除く) 。たとえば、メソッドの宣言を Exception クラスに移動した場合、Exception のサブタイプで同じ問題が繰り返されるだけです。ただし、ジェネリック型パラメーターを使用してシグネチャを表現しようとすると、Throwable の明示的なジェネリック型のすべてのサブクラスを作成する必要があります。

幸いなことに、治療法は非常に簡単です。fillInStacktrace()の結果を次のようにキャストします。

public void function() throws Exception {
    try {
        throw new Exception();
    } catch (Exception ex) {
        throw (Exception) (ex.fillInStackTrace());
    }
}

そして最後のポイントは、アプリケーションが明示的に を呼び出すことは非常に珍しいということですfillInStacktrace()。このことを考えると、Java 設計者がこれを解決しようとして「腹をくくった」ことには、まったく価値がありませんでした。特に、それは本当に些細な不便にすぎないので... せいぜい。

于 2010-01-08T06:33:04.493 に答える
6

実際には、質問 2 から始めて質問に答える方が簡単です。

質問: 2. Exception 参照を受け取る例外ハンドラーが Throwable 例外を処理できるように設計されているため、メソッド g() が Throwable 例外をスローするとマークする必要はありません。

回答: 実際には、catch( Exception e) は Throwable をキャッチできません。これを試して:

try {
       Throwable t = new Throwable();
       throw t.fillInStackTrace();
} catch (Exception e) {
    System.out.println("outer exception handler");
    e.printStackTrace();
} 

この場合、catch 句がスローをキャッチしていないことがわかります。

g() メソッドで catch 句が機能する理由は、 を呼び出すthrow e.fillInStackTrace()と、fillInStackTrace の呼び出しが実際に例外を返すためです (これは、e 自体が例外であるためです)。Exception は Throwable のサブクラスなので、fillInStackTrace の宣言と矛盾しません。

では最初の質問へ

あなたは尋ねました: 1. Exception.fillInStackTrace() は Throwable 参照を返すので、Exception 参照を受け取る例外ハンドラーは例外をキャッチできないはずです。 fillInstackTrace()。

回答: これは厳密には暗黙のダウンキャストではありません。これはオーバーロードのバリエーションと考えてください。

あなたが持っているとしましょう

void process(Throwable t){
   ...
}
void process(Exception e){
  ...
} 

を呼び出すとprocess(someObject)、1 番目または 2 番目のプロセス メソッドが呼び出されるかどうかが実行時に決定されます。同様に、catch(Exception e) 句がスローをキャッチできるかどうかは、Exception をスローするか、Throwable をスローするかに基づいて、実行時に決定されます。

于 2010-01-14T19:00:02.963 に答える
3

fillInStackTrace同じオブジェクトへの参照を返します。Exception例外のスタック トレースを再スローしてリセットできるようにするのは、メソッド チェーンです。

public static void m() {
    throw new RuntimeException();

}

public static void main(String[] args) throws Throwable {
    try {
        m();
    } catch (Exception e) {
        e.printStackTrace();
        throw e.fillInStackTrace();
    }
}

メソッドの戻り値の型は、 である基本型のみにすることができますThrowable。メソッドがパラメーター化された型を返すようにジェネリック化できます。しかし、今のところそうではありません。

RuntimeException e = new RuntimeException();
Throwable e1 = e.fillInStackTrace();
System.out.println(e1.getClass().getName()); //prints java.lang.RuntimeException
System.out.println(e == e1); //prints true
于 2010-01-08T05:18:24.983 に答える
2

実際fillInStackTraceには、呼び出されたのと同じオブジェクトを返します。

e.fillInStackTrace == e常に真です

それはただのショートカットです、あなたはまた書くことができます

    } catch (Exception e) {
        System.out.println("inner exception handler");
        e.fillInStackTrace();
        throw e;
    }

またはキャストを使用する

    throw (Exception) e.fillInStackTrace();

ところで、同じことが起こりinitCause()ます。

于 2010-01-11T09:51:16.660 に答える
2

fillInStackTrace()Throwableではなくによって定義されExceptionます。

のすべてのサブクラスThrowableが例外であるとは限りません (Errorなど)。に関する限りThrowable、戻り値について保証できる唯一のことfillInStackTrace()は、それがのインスタンスであることです( Chandra PatniThrowableが指摘したように、同じオブジェクトを返すだけなので)。

于 2010-01-08T05:23:37.703 に答える