Javaで投げられる拡張可能
-
02-10-2019 - |
質問
Javaを使用すると、まったく新しいサブタイプを作成できます Throwable
, 、例:
public class FlyingPig extends Throwable { ... }
今、 めったにありません, 、私はこのようなことをするかもしれません:
throw new FlyingPig("Oink!");
そしてもちろん他の場所:
try { ... } catch (FlyingPig porky) { ... }
私の質問は次のとおりです。
- これは悪い考えですか?もしそうなら、なぜですか?
- 悪い考えである場合、このサブタイピングを防ぐために何ができたでしょうか?
- それは(私が知る限り)予防可能ではないので、どのような大惨事が生じる可能性がありますか?
- これがそれほど悪い考えでないなら、なぜですか?
- どうすればあなたができるという事実から何か有用なものを作ることができますか
extends Throwable
?
- どうすればあなたができるという事実から何か有用なものを作ることができますか
提案されたシナリオ#1
私がいたシナリオ 本当 このようなことをするように誘惑されています。次のプロパティがあります。
- 「イベント」はそれです 意思 最終的に起こります。そうです 期待される. 。それは間違いなくそうではありません
Error
, 、そして何もありませんException
- それが発生するときについて。- その理由は 期待される, 、aがあります
catch
それを待っています。それは何も「スリップ」しません。それはどんな試みからも「逃げる」ことはありませんcatch
全般的Exception
および/またはError
.
- その理由は 期待される, 、aがあります
- 「イベント」が発生します 非常にまれです.
- それが起こると、通常、深いスタックトレースがあります。
だからおそらく、私が言おうとしていることは今明らかです: FlyingPig
それは 結果 徹底的な再帰検索の。
検索されるオブジェクトは存在します。検索スペースである大きな海でそれを見つけることの問題だけです。検索プロセスは長いものになるため、例外処理の比較的高価なコストはごくわずかです。実際、従来のコントロールフロー構成は、 boolean isFound
フラグは、検索プロセス全体で継続的にチェックする必要があるため、おそらく再帰のあらゆるレベルで継続的にチェックする必要があるため、より高価になる場合があります。このチェックは99.99%の時間に失敗しますが、終了条件を伝播することは絶対に必要です。ある意味で 効果的, 、チェックはです 非効率的な!
単に throw
-ing a FlyingPig
求められているオブジェクトが見つかったとき、あなたはの管理でコードを乱雑にする必要はありません boolean isFound
国旗。その点でコードがクリーンであるだけでなく、この省略によりより速く実行される可能性があります。
要約すると、選択はこれら2つの間にあります。
- 従来のコントロールフローアプローチ
- 使う
boolean isFound
, 、継続的にチェックします - 99.99%の時間、チェックは「廃棄物」です。
false
- それが最終的になるとき
true
, 、再発を停止し、最初の呼び出しに適切にリラックスできることを確認する必要があります。
- 使う
FlyingPig
アプローチ- 気にしないでください
boolean isFound
. - 見つかった場合、ちょうど
throw new FlyingPig()
;これは 期待される, 、したがって、aがありますcatch
それのための。 - の管理はありません
boolean
旗、無駄なチェックを続ける必要があるかどうか、再帰を手動で巻き戻すための簿記はありません。
- 気にしないでください
質問:
- この技術は、例外を使用して有効ですか? (その名前はありますか?)
- 有効な場合は、必要です
FlyingPig extends Throwable
, 、またはですException
大丈夫? (その状況について例外的なものは何もありませんが?)
解決
本当に悪い考えだと思います。多くのコードが、あなたがキャッチした場合の仮定に基づいて実装されています Error
と Exception
すべての可能な例外をキャッチしました。そして、ほとんどのチュートリアルと教科書はあなたに同じことを教えてくれます。の直接サブクラスを作成します Throwable
あらゆる種類のメンテナンスと相互運用性の問題を作成する可能性があります。
拡張する正当な理由はないと思います Throwable
. 。拡張する Exception
また RuntimeException
代わりは。
編集 -OPの提案されたシナリオ#1に応じて。
例外は、「通常の」フロー制御に対処するための非常に高価な方法です。場合によっては、私たちは話しています 数千 例外を作成、スロー、キャッチするために実行された追加の指示の。受け入れられている知恵を無視し、非エクシセントフロー制御に例外を使用する場合は、 Exception
サブタイプ。何かのふりをしようとすることは、「例外」ではなく「イベント」であり、のサブタイプとしての「例外」ではありません Throwable
何も達成するつもりはありません。
ただし、例外をエラー、間違い、間違ったものと混同することは間違いです。そこには 何も間違っていません 「エラー、間違い、間違ったものではない例外的なイベントを表す」 Exception
. 。重要なのは、イベントがあるべきだということです 例外的;つまり、普通ではなく、非常にまれに起こっています...
要約すると、a FlyingPig
エラーではないかもしれませんが、それをサブタイプとして宣言しない理由はありません Exception
.
他のヒント
この技術は、例外を使用して有効ですか? (その名前はありますか?)
私の意見では、それは良い考えではありません:
- それは違反します 最も驚きの原則, 、特に例外がない場合は、コードの読み取りが難しくなります。
- スローの例外は、Javaの非常に高価な操作です(ただし、ここでは問題ではないかもしれません)。
例外はただなければなりません フロー制御には使用されません. 。この手法に名前を付けなければならなかった場合、コードの匂いまたはアンチパターンと呼びます。
参照:
有効な場合は、必要です
FlyingPig
拡張Throwable
, 、またはですException
大丈夫? (その状況について例外的なものは何もありませんが?)
あなたがしたい状況があるかもしれません キャッチ Throwable
捕まえるだけではありません Exception
だけでなく、 Error
しかし、それはまれであり、人々は通常捕まえません Throwable
. 。しかし、私はあなたがしたい状況を見つけることに失敗しました 投げる のサブクラス Throwable
.
また、私はそれを拡張すると思います Throwable
物事が少なく見えるようにしません 「例外 - 」, 、彼らはそれを悪化させます - それは完全に意図を打ち負かします。
結論として、本当に何かを投げたいなら、のサブクラスを投げます Exception
.
参照:
これは、ホットスポットアーキテクトのジョンローズのブログ投稿です。
http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpiense
これは、フロー制御のための「乱用」の例外についてです。わずかに異なるユースケースですが、..要するに、それは非常にうまく機能します - スタックトレースが作成されるのを防ぐために例外を事前に/クローンする場合。
クライアントから「隠された」場合、この手法は正当化できると思います。つまり、FlyingPigはライブラリを離れることができないはずです(すべてのパブリック方法は、それを投げることを一時的に保証する必要があります)。これを保証する1つの方法は、それをチェックされた例外にすることです。
投げられる唯一の正当化は、キャッチ(例外e)条項があるコールバックを人々に渡すことを許可したいと思うからだと思います。私はそれを買うことができます...
org.junit.Test
注釈には None
拡張するクラス Throwable
デフォルト値として使用されます expected
注釈パラメーター。
エラーと例外の両方からFlyingPigを設定するものを正当化できる場合、どちらのサブクラスとして適していないようにする場合、それを作成することに根本的に間違っていることはありません。
私が考えることができる最大の問題は、実用的な世界にあります。時にはJava.lang.Exceptionを捕まえる正当な理由があります。あなたの新しいタイプのスロー可能なものは、すべての致命的でない問題を抑制(または伐採、ラッピングなど)というあらゆる合理的な期待を持っていたトライキャッチブロックを通り過ぎて飛ぶことになるでしょう。
あなたが古いシステムでメンテナンスをしているなら、逆に 国連Java.lang.exceptionを正当に抑制すると、あなたはそれをだまします。 (実際にそれを適切に修正するための誠実な魅力が拒否されると仮定することは拒否されます)。
この質問が進化するにつれて、私は元の質問のポイントを誤解していることがわかります。そのため、他の答えのいくつかはおそらくより関連性があります。同様の質問をしている他の人にも役立つかもしれないので、この答えをここに任せ、彼らの質問に対する答えを探している間にこのページを見つけます。
スロー可能な延長には、カスタムの例外を投げる(そして処理する)ことができるようにすることには何の問題もありません。ただし、次のポイントを念頭に置いておく必要があります。
- 可能な限り最も具体的な例外を使用することは素晴らしいアイデアです。これにより、発信者はさまざまな種類の例外を異なる方法で処理できます。例外のクラス階層は留意するために重要であるため、カスタム例外は、可能な限り例外に近い別のタイプのスロー可能なものを拡張する必要があります。
- スロー可能な拡張は高すぎる場合があります。代わりに、例外またはruntimeexceptionを拡張してみてください(または、例外をスローしている理由に近い低レベルの例外)。 RuntimeExceptionと例外の違いに留意してください。
- 例外(または例外のサブクラス)をスローするメソッドへの呼び出しは、例外を扱うことができるTry/Catchブロックにラップする必要があります。これは、あなたの制御不能になる可能性のある状況のために、物事がうまくいかないと予想される場合に適しています(例えば、ネットワークがダウンしています)。
- RuntimeException(またはそのサブクラス)をスローするメソッドへの呼び出しは、例外を処理できるTry/Catchブロックにラップする必要はありません。 (確かにそうかもしれませんが、そうする必要はありません。)これは、実際には予想されるべきではない例外のためです。
したがって、コードベースに次の例外クラスがあるとします。
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) {
...
}
遊ぶ!フレームワーク リクエスト処理にはこのようなものを使用します。要求処理は、多くのレイヤー(ルーティング、ミドルウェア、コントローラー、テンプレートレンダリング)を通過し、最終レイヤーでレンダリングされたHTMLは スロー可能なものに包まれています そして、最も上のレイヤーがキャッチするスローされ、包まれてクライアントに送信されます。したがって、関係する多くのレイヤーのメソッドのいずれも、応答オブジェクトを明示的に返す必要はなく、伝播および変更される引数として応答オブジェクトを渡す必要もありません。
私は詳細について少し大ざっぱです。プレイコードを調べることができます!詳細についてはフレームワーク。