Exception.fillInStackTrace が Throwable を返すのはなぜですか?
-
19-09-2019 - |
質問
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();
}
}
- の
exception handler
を捕まえることができませんでしたnew Throwable()
最初の関数でf()
. - の
exception handler
を捕まえることができたe.fillInstackTrace()
2番目の関数でg()
. - しかし、2番目の関数は
g()
まだ必要だろうthrows Throwable
. 。これは本当に不思議です。e.fillInstackTrace()
.
そこで私の質問は、なぜこのような奇妙な構文を開発せずに、Exception.fillInStackTrace が例外または例外派生を返さないのかということです。
編集:
に 明らかにする 私の質問:「奇妙な構文」とは次のことを意味します。
- 以来
Exception.fillInStackTrace()
戻るThrowable
参照、受け取る例外ハンドラException
参照は例外をキャッチできません。Java では暗黙的なダウンキャストが許可されていないため、次のようにする必要があります。return (Exception)e.fillInstackTrace()
. - 例外ハンドラが受け取るように設計されているため、
Exception
参照で処理できるThrowable
例外として、メソッドをマークする必要はありませんg()
投げるThrowable
ただし、Java コンパイラはそうすることを強制します。
ありがとう。
解決
これは、質問2で始まる、あなたの質問に答えるために、実際に簡単です。
あなたは尋ねました: 2.例外の参照が供給例外ハンドラがThrowableの例外を扱うことができ、(メソッドgをマークする必要がないように設計されているため)がThrowableをexception.ButのJavaコンパイラがそうするために私達を強制するでしょうスローます。
回答: 実際には、キャッチ(例外e)はThrowableのをキャッチすることはできません。 これを試してください:
try {
Throwable t = new Throwable();
throw t.fillInStackTrace();
} catch (Exception e) {
System.out.println("outer exception handler");
e.printStackTrace();
}
あなたはcatch節は、この場合にスローをキャッチしていないことがわかります。
catch節があなたのグラム()メソッドで動作していることの理由は、あなたがthrow e.fillInStackTrace()
を呼び出すとき、にfillInStackTraceへの呼び出しが実際に例外(eは例外自体があるので、それはだ)を返すということです。例外はThrowableのサブクラスであるため、それはにfillInStackTraceの宣言と矛盾しない。
これで最初の質問への
あなたは尋ねました: Exception.fillInStackTrace以来1.()のThrowable参照を返す、例外への参照を受け取る例外ハンドラがexception.Becauseのjavaはimplictが意気消沈ことはできません、それはリターン(例外)e.fillInstackTrace(のようなものでなければなりませんキャッチすることはできません)ます。
回答: これはまさに、暗黙的なダウンキャストではありません。オーバーロードの変動と考える。
あなたが持っているとしましょう。
void process(Throwable t){
...
}
void process(Exception e){
...
}
あなたがprocess(someObject)
を呼び出す場合は、、第一又は第二の処理方法が呼び出されるかどうかを実行時に決定されます。同様に、キャッチ(例外e)の句があなたのスローをキャッチできるかどうか、あなたが例外かのThrowableを投げるかどうかに基づいて、実行時に決定されます。
他のヒント
あなたの質問にはかなり困惑しています。Java の例外/例外処理について、明らかに理解できない点があります。それでは、最初から始めましょう。
Java では、すべての例外 (この用語が Java 言語仕様で使用されるという意味で) は、クラスのサブクラスであるクラスのインスタンスです。 java.lang.Throwable
. 。Throwable の直接のサブクラスは 2 つ (そして 2 つだけ) あります。つまり java.lang.Exception
そして java.lang.Error
. 。これらすべてのクラスのインスタンス...Throwable と Error のインスタンスを含む ...JLS では例外として参照されます。
例外ハンドラーは、例外ハンドラーで使用される例外タイプと割り当て互換性のある例外 (JLS の意味で) をキャッチします。 catch
宣言。たとえば、次のようになります。
try {
....
} catch (Exception ex) {
...
}
でスローされた例外をキャッチします。 try
のインスタンスであるブロック java.lang.Exception
または、直接的または間接的なサブタイプの java.lang.Exception
. 。しかし、それはインスタンスをキャッチしません java.lang.Throwable
, 、それは(明らかに)上記のどれでもないからです。
一方で:
try {
....
} catch (Throwable ex) {
...
}
意思 ~のインスタンスをキャッチする java.lang.Throwable
.
これを踏まえて例を確認すると、その理由は明らかです。 f
メソッドが Throwable インスタンスをキャッチしていません:catch 句の例外タイプと一致しません。対照的に、 g
メソッドでは、Exception インスタンスが catch 句の例外タイプと一致したため、キャッチされます。
Throwable をスローする必要があることについて何を言っているのかわかりません g
. 。まず、メソッドが Throwable をスローすることを宣言しているという事実 ではない 実際にそれをスローする必要があることを意味します。それが言っているのはそれだけです かもしれない Throwable に割り当て可能なものをスローします ...おそらく将来のバージョンでは、 g
方法。第二に、追加するとしたら throw e;
外側のキャッチブロックに、 するだろう 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.
}
}
これは少々予想外であることがわかります。しかし、それは残念ながら避けられません。Java の型システムに大きな変更を加えない限り、その署名を宣言する方法はありません。 fillInStacktrace
それはあらゆる場合に機能します。たとえば、メソッドの宣言を Exception クラスに移動した場合、Exception のサブタイプで同じ問題を繰り返すだけになります。ただし、ジェネリック型パラメーターを使用してシグネチャを表現しようとすると、Throwable の明示的なジェネリック型のすべてのサブクラスを作成する必要があります。
幸いなことに、治療法は非常に簡単です。の結果をキャストする fillInStacktrace()
次のように:
public void function() throws Exception {
try {
throw new Exception();
} catch (Exception ex) {
throw (Exception) (ex.fillInStackTrace());
}
}
そして最後のポイントは、アプリケーションが明示的に呼び出しを行うことは非常に珍しいということです。 fillInStacktrace()
. 。それを考慮すると、Java 設計者がこれを解決しようとして「根性を潰した」のはまったく無駄でした。特にそれは実際にはほんの小さな不便なので...せいぜい。
私は、あなたがフランク・イェリン(@author
のjava.lang.Exception
を)聞いていると思います。他の人が言ったように、fillInStackTrace()
は、その戻り値の型がThrowable
しなければならないので、this
で宣言され、Throwable
を返すために文書化されています。それはちょうどException
によって継承されています。フランクは、このように、Exception
でそれをオーバーライドしている可能性がます:
public class Exception extends Throwable{
/**Narrows the type of the overridden method*/
@Override
public synchronized Exception fillInStackTrace() {
super.fillInStackTrace();
return this;
}
}
...しかし、彼はしませんでした。 Javaの1.5の前に、その理由は、それがコンパイルエラーを生成しているだろうということです。 1.5以降では、共変戻り値の型はと上記が許可されています法的ます。
しかし、私の推測では、上記のオーバーライドが存在する場合RuntimeException
そうにそのオーバーライドをオーバーライドし、しない理由ArrayStoreException
は、同様のオーバーライドを持っていない理由を、あなたは尋ねることだろうということです。だから、フランクと友人はおそらくちょうど同じオーバーライドのそれらの何百ものを書きたくありませんでした。
それはあなたが独自のカスタム例外クラスを扱っている場合、あなたは簡単に私は上記のやったようfillinStackTrace()
メソッドをオーバーライドして、あなたが後にしている狭いタイプを得ることができる、ということは注目に値します。
ジェネリックを使用して、一つはにThrowable
の宣言を増強想像できます
public class Throwable<T extends Throwable<T>>{
public synchronized native T fillInStackTrace();
}
...しかし、それはこの答えの範囲を超えての理由のために、本当に満足のいくものではないのです。
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
fillInStackTrace()
によって定義されます Throwable
, 、 ない Exception
.
のすべてのサブクラスではありません Throwable
は例外です (Error
, 、 例えば)。の限り Throwable
懸念されるのは、戻り値についてのみ保証できることです。 fillInStackTrace()
それはの例だということです Throwable
(同じオブジェクトを返すだけなので、 チャンドラ・パトニ 了解しました)。
実際にそれが呼び出されたのと同じオブジェクトを返しますfillInStackTrace
。
e.fillInStackTrace == e
は常に真である
それはちょうど、のショートカットです。の、あなたはまた、単に
書くことができます } catch (Exception e) {
System.out.println("inner exception handler");
e.fillInStackTrace();
throw e;
}
または
キャストを使用 throw (Exception) e.fillInStackTrace();
ところで、同じことがinitCause()
で発生します。