Javaでは、Throwable
のまったく新しいサブタイプを作成できます。例:
_public class FlyingPig extends Throwable { ... }
_
さて、めったに、私はこのようなことをするかもしれません:
_throw new FlyingPig("Oink!");
_
そしてもちろん他の場所:
_try { ... } catch (FlyingPig porky) { ... }
_
私の質問は次のとおりです。
extends Throwable
_できるという事実から、どうすれば何か役に立つものを作ることができますか?私が本当にこのようなことをしたいと思ったシナリオには、次のプロパティがあります。
Error
ではなく、いつ発生するかについてはException
- alは何もありません。catch
が待機します。何もすり抜けることはありません。 catch
general Exception
および/またはError
への試行から「エスケープ」することはありません。だから、おそらく今私が言おうとしていることは明らかです:FlyingPig
は徹底的な再帰検索の結果です。
検索対象は存在します。検索スペースである大海原でそれを見つけるだけです。検索プロセスは長いものになるため、例外処理の比較的高価なコストはごくわずかです。実際、_boolean isFound
_フラグを使用する代わりの従来の制御フロー構造は、検索プロセス全体で、おそらく再帰のすべてのレベルで継続的にチェックする必要があるため、よりコストがかかる可能性があります。このチェックは99.99%の確率で失敗しますが、終了条件を伝播することが絶対に必要です。ある意味で、有効であるのに対し、チェックは非効率です!
探しているオブジェクトが見つかったときにthrow
を単にFlyingPig
-するだけで、_boolean isFound
_フラグの管理でコードを乱雑にする必要はありません。その点でコードがクリーンになるだけでなく、この省略によりコードの実行速度が上がる可能性があります。
要約すると、選択はこれら2つの間です。
boolean isFound
_を使用しますfalse
であるためです。true
になると、繰り返しを停止し、最初の呼び出しに適切に巻き戻すことができることを確認する必要があります。FlyingPig
アプローチboolean isFound
_を気にしないでください。throw new FlyingPig()
; expectedなので、catch
があります。boolean
フラグの管理、続行する必要があるかどうかの無駄なチェック、再帰を手動で巻き戻すための簿記などはありません。質問:
FlyingPig extends Throwable
_である必要がありますか、それともException
で問題ありませんか? (その状況について例外的なことは何もありませんが?)それは本当に悪い考えだと思います。 Error
とException
をキャッチすると、考えられるすべての例外をキャッチしたことを前提に、多くのコードが実装されます。そして、ほとんどのチュートリアルと教科書はあなたに同じことを教えてくれます。 Throwable
の直接サブクラスを作成することにより、あらゆる種類のメンテナンスと相互運用性の問題が発生する可能性があります。
Throwable
を拡張する正当な理由は考えられません。代わりにException
またはRuntimeException
を拡張してください。
[〜#〜] edit [〜#〜]-OPの提案されたシナリオ#1への応答。
例外は、「通常の」フロー制御を処理するための非常にコストのかかる方法です。場合によっては、例外を作成、スロー、およびキャッチするために実行される数千の追加命令について話します。受け入れられている知識を無視し、例外的でないフロー制御に例外を使用する場合は、Exception
サブタイプを使用します。 Throwable
のサブタイプは何も達成しないため、何かを偽装しようとすると、「例外」ではなく「イベント」であると宣言します。
ただし、例外をエラー、間違い、間違ったものなどと混同するのは間違いです。そして、Exception
のサブクラスを使用して、「エラー、間違い、間違ったものなどではない例外的なイベント」を表すことには、何も悪いことはありません。重要なのは、イベントがexceptionalである必要があるということです。つまり、異常な、非常にまれにしか発生しない、.。
要約すると、FlyingPig
はエラーではないかもしれませんが、それをException
のサブタイプとして宣言しない理由はありません。
例外を(ab)使用するこの手法は有効ですか? (名前はありますか?)
私の意見では、それは良い考えではありません:
例外は フロー制御には使用されません である必要があります。この手法に名前を付ける必要がある場合は、コードの臭いまたはアンチパターンと呼びます。
参照:
有効な場合、
FlyingPig
はThrowable
を拡張する必要がありますか、それともException
で問題ありませんか? (その状況について例外的なことは何もありませんが?)
catchThrowable
をキャッチするだけでなく、Exception
だけでなくError
もキャッチしたい場合がありますが、それはまれです。 、人々は通常Throwable
をキャッチしません。しかし、私はあなたがしたい状況を見つけるのに失敗しますthrowThrowable
のサブクラス。
また、Throwable
を拡張しても、見た目が悪くなることはなく "exception-al"、見栄えが悪くなると思います。
結論として、本当に何かをスローしたい場合は、Exception
のサブクラスをスローします。
参照:
HotSpotアーキテクトのJohnRoseからのブログ投稿は次のとおりです。
http://blogs.Oracle.com/jrose/entry/longjumps_considered_inexpensive
これは、フロー制御の例外を「乱用」することについてです。ユースケースは少し異なりますが、..要するに、スタックトレースが作成されないように例外を事前に割り当て/複製すると、非常にうまく機能します。
この手法は、クライアントから「隠されている」場合には正当であると思います。 IE、FlyingPigがライブラリを離れることができないようにする必要があります(すべてのパブリックメソッドは、ライブラリをスローしないことを推移的に保証する必要があります)。これを保証する1つの方法は、チェックされた例外にすることです。
Throwableを拡張する唯一の理由は、catch(Exception e)句を含むコールバックをユーザーが渡せるようにし、結果が無視されるようにするためだと思います。私はそれを買うことができます...
org.junit.Test
アノテーションには、None
を拡張するThrowable
クラスが含まれ、expected
アノテーションパラメータのデフォルト値として使用されます。
FlyingPigをErrorとExceptionの両方と区別して、どちらのサブクラスとしても適さないようにすることができるのであれば、それを作成することに根本的な問題はありません。
私が考えることができる最大の問題は、実用的な世界にあります。Java.lang.Exceptionをキャッチする正当な理由がある場合があります。あなたの新しいタイプのスローアブルは、致命的ではない可能性のあるすべての問題を抑制する(またはロギング、ラッピングなど)という合理的な期待をすべて持っていたtry-catchブロックを通り過ぎて飛ぶでしょう。
逆に、n Java.lang.Exceptionを正当に抑制している古いシステムでメンテナンスを行っている場合は、それをごまかすことができます。 (実際に適切に修正するための時間に対する誠実な訴えは否定されます)。
Play!framework は、リクエスト処理にこのようなものを使用します。リクエスト処理は多くのレイヤー(ルーティング、ミドルウェア、コントローラー、テンプレートレンダリング)を通過し、最後のレイヤーでレンダリングされたHTMLは スロー可能にラップされます そしてスローされます。これは最上位のレイヤーがキャッチ、アンラップ、送信します。クライアントに。したがって、関係する多くのレイヤーのメソッドはいずれも、応答オブジェクトを明示的に返す必要はありません。また、伝播および変更するために、引数として応答オブジェクトを渡す必要もありません。
私は細部について少し大ざっぱです。 Playのコードを確認できます。詳細についてはフレームワーク。
この質問が進化するにつれて、私は元の質問のポイントを誤解していることがわかります。したがって、他の回答のいくつかはおそらくより適切です。同様の質問をしている他の人にも役立つ可能性があるため、この回答はここに残しておき、質問に対する回答を検索しているときにこのページを見つけます。
Throwableを拡張して、カスタム例外をスロー(および処理)できるようにすることには何の問題もありません。ただし、次の点に注意する必要があります。
したがって、コードベースに次の例外クラスがあるとします。
_public class Pig extends Throwable { ... }
public class FlyingPig extends Pig { ... }
public class Swine extends Pig { ... }
public class CustomRuntimeException extends RuntimeException { ... }
_
そしていくつかの方法
_public void foo() throws Pig { ... }
public void bar() throws FlyingPig, Swine { ... }
// suppose this next method could throw a CustomRuntimeException, but it
// doesn't need to be declared, since CustomRuntimeException is a subclass
// of RuntimeException
public void baz() { ... }
_
これで、次のようなこれらのメソッドを呼び出すコードを作成できます。
_try {
foo();
} catch (Pig e) {
...
}
try {
bar();
} catch (Pig e) {
...
}
baz();
_
bar()
を呼び出すと、Pig
とFlyingPig
の両方がSwine
を拡張するため、Pig
をキャッチできることに注意してください。これは、どちらかの例外を処理するために同じことをしたい場合に役立ちます。ただし、別の方法で処理することもできます。
_try {
bar();
} catch (FlyingPig e) {
...
} catch (Swine e) {
...
}
_