31

Java では、 のまったく新しいサブタイプを作成できますThrowable。たとえば、次のようになります。

public class FlyingPig extends Throwable { ... }

さて、ごくまれに、私は次のようなことをするかもしれません:

throw new FlyingPig("Oink!");

そしてもちろん他の場所:

try { ... } catch (FlyingPig porky) { ... }

私の質問は次のとおりです。

  • これは悪い考えですか?もしそうなら、なぜですか?
    • このサブタイピングが悪い考えである場合、それを防ぐために何ができたでしょうか?
    • (私の知る限り)防ぐことはできないため、どのような大惨事が発生する可能性がありますか?
  • これがそれほど悪い考えではないのなら、なぜですか?
    • できるという事実から、どうすれば何か役立つものを作ることができるextends Throwableでしょうか?

提案されたシナリオ #1

私が本当にこのようなことをしたいと思ったシナリオには、次の特性があります。

  • 「出来事」とは、やがて起こるものです期待されています。それは間違いなく ではなく、いつ発生するかについては Error何もありません。Exception
    • 期待されているので、catch待ち遠しいです。それは何かを「すり抜ける」ことはありません。catch一般的なExceptionおよび/またはへの試みから「逃げる」ことはありませんError
  • 「イベント」はごくまれに発生します。
  • これが発生すると、通常、深いスタック トレースが存在します。

したがって、おそらく、私が言おうとしていることは明らかです。それは、徹底的な再帰的検索FlyingPig結果です。

捜索対象は存在する:捜索空間である大海原でそれを見つければよい。検索プロセスは時間がかかるため、比較的高価な例外処理のコストは無視できます。実際、フラグを使用する従来の制御フロー構造の代替手段はboolean isFound、再帰のすべてのレベルで、検索プロセス全体で継続的にチェックする必要があるため、よりコストがかかる可能性があります。このチェックは 99.99% の確率で失敗しますが、終了条件を伝播することは絶対に必要です。ある意味では、効果的ですが、チェックは非効率的です!

探しているオブジェクトが見つかったときに単にthrowaを実行するだけで、フラグの管理でコードを混乱させる必要がなくなります。その点でコードがきれいになるだけでなく、この省略により実行速度が向上する可能性があります。FlyingPigboolean isFound

要約すると、次の 2 つのいずれかを選択します。

  • 従来の制御フロー アプローチ
    • を使用しboolean isFound、継続的にチェック
    • 99.99% の確率で、小切手は「無駄」です。false
    • 最終的に になるとtrue、再帰を停止し、最初の呼び出しに適切に巻き戻すことができることを確認する必要があります。
  • FlyingPigアプローチ
    • 気にしないでくださいboolean isFound
    • 見つかった場合は、ただthrow new FlyingPig(); それは期待されているので、それには がありますcatch
    • フラグの管理、boolean続行する必要がある場合の無駄なチェック、再帰を手動で巻き戻す簿記などはありません。

質問:

  • (ab)例外を使用するこの手法は有効ですか? (名前はありますか?)
  • 有効な場合、必要ですかFlyingPig extends Throwable、それともException問題ありませんか? (その状況に例外は何もないのに?)
4

7 に答える 7

36

それは本当に悪い考えだと思います。多くのコードは、可能性のあるすべての例外をキャッチし、キャッチErrorした場合を想定して実装されています。Exceptionそして、ほとんどのチュートリアルや教科書は同じことを教えてくれます。あなたの直接のサブクラスを作成するThrowableと、あらゆる種類のメンテナンスと相互運用性の問題が発生する可能性があります。

延長する正当な理由が思いつきませんThrowable。拡張ExceptionまたはRuntimeException代わりに。

編集 - OPの提案されたシナリオ#1に応じて。

例外は、「通常の」フロー制御を処理するための非常にコストのかかる方法です。場合によっては、例外を作成、スロー、およびキャッチするために実行される何千もの追加命令について話していることもあります。受け入れられている知恵を無視し、例外ではないフロー制御に例外を使用する場合は、Exceptionサブタイプを使用してください。何かを「例外」ではなく「イベント」であるかのように見せかけようとして、 is をサブタイプとして宣言しても、Throwable何も達成されません。

ただし、例外をエラー、間違い、間違いなどと混同するのは誤りです。そして、 のサブクラスを使用して「エラー、間違い、間違いなどではない例外的なイベント」を表現することにExceptionの問題もありません。重要なのは、イベントが例外的でなければならないということです。つまり、通常とは異なり、非常にまれにしか発生しません...

要約すると、 aFlyingPigはエラーではないかもしれませんが、だからといって a を のサブタイプとして宣言しない理由にはなりませんException

于 2010-05-03T03:41:17.690 に答える
8

(ab)例外を使用するこの手法は有効ですか? (名前はありますか?)

私の意見では、それは良い考えではありません:

  • これは最小の驚きの原則に違反しており、コードが読みにくくなります。特に例外がない場合はなおさらです。
  • 例外のスローは、Java では非常にコストのかかる操作です (ここでは問題にならないかもしれません)。

例外はフロー制御には使用しないでください。この手法に名前を付けるとしたら、コードの匂いまたはアンチ パターンと呼ぶでしょう。

以下も参照してください。

有効な場合は、FlyingPig拡張する必要がありますかThrowable、それともException問題ありませんか? (その状況に例外は何もないのに?)

ただ釣るだけでなく、稀に釣るため釣る 場面もあるかもしれませんが、普段は釣れない人が多いです。しかし、 のサブクラスをスローしたい状況を見つけることができません。ThrowableException ErrorThrowableThrowable

また、拡張によって物事が「例外的」Throwableに見えなくなるわけではなく、見た目が悪くなると思います-これは意図を完全に打ち負かします.

結論として、本当に何かをスローしたい場合は、 のサブクラスをスローしExceptionます。

以下も参照してください。

于 2010-05-03T07:16:44.637 に答える
7

以下は、HotSpot アーキテクトの John Rose によるブログ投稿です。

http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpensive

フロー制御の例外を「悪用」することです。ユースケースは少し異なりますが..要するに、スタックトレースが作成されないように例外を事前に割り当て/複製すると、非常にうまく機能します。

クライアントから「隠されている」場合、この手法は正当化されると思います。IE の場合、FlyingPig はライブラリを離れることはできません (すべてのパブリック メソッドは推移的にそれをスローしないことを保証する必要があります)。これを保証する 1 つの方法は、それをチェック済みの例外にすることです。

Throwable を拡張する唯一の理由は、人々が catch(Exception e) 句を持つコールバックを渡すことを許可したいためであり、結果が無視されることを望んでいるためだと思います。私はそれを買うことができます...

于 2010-08-30T12:49:01.987 に答える
4

org.junit.Test注釈には、注釈パラメータのデフォルト値としてNone拡張および使用されるクラスが含まれます。Throwableexpected

于 2010-05-03T03:40:53.097 に答える
4

FlyingPig を Error と Exception の両方から切り離して、どちらのサブクラスとしても適していないことを正当化できる場合は、FlyingPig を作成することに根本的な問題はありません。

私が考えることができる最大の問題は、実用的な世界にあります.java.lang.Exceptionをキャッチする正当な理由がある場合があります。あなたの新しいタイプの throwable は、可能なすべての非致命的な問題を抑制する (またはロギング、ラッピングなど) というあらゆる合理的な期待を持っていた try-catch ブロックを通り過ぎて飛んでいきます。

反対に、 java.lang.Exceptionを不当に抑制している古いシステムでメンテナンスを行っている場合は、ごまかすことができます。(実際に適切に修正するための時間の誠実なアピールが拒否されたと仮定します)。

于 2010-05-03T03:48:59.173 に答える
2

この質問が発展するにつれて、元の質問の要点を誤解していることがわかったので、他の回答のいくつかはおそらくより関連性があります. この回答はここに残しておきます。同様の質問をしている他の人にとってまだ役立つ可能性があり、質問に対する回答を検索しているときにこのページを見つけたからです。

カスタム例外をスロー (および処理) できるように Throwable を拡張しても問題はありません。ただし、次の点に注意してください。

  • 可能な限り具体的な例外を使用することは素晴らしいアイデアです。これにより、呼び出し元はさまざまな種類の例外を異なる方法で処理できます。例外のクラス階層に留意することが重要であるため、カスタム例外は、例外にできるだけ近い別のタイプの Throwable を拡張する必要があります。
  • Throwable の拡張はレベルが高すぎる可能性があります。代わりに Exception または RuntimeException (または、例外をスローする理由により近い低レベルの例外) を拡張してみてください。RuntimeException と Exception の違いに注意してください。
  • Exception (または Exception のサブクラス) をスローするメソッドの呼び出しは、例外を処理できる 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();

を呼び出すときは、両方とも extends であるため、単にcatchbar()できることに注意してください。これは、いずれかの例外を処理するために同じことをしたい場合に便利です。ただし、それらを別の方法で処理することもできます。PigFlyingPigSwinePig

try {
    bar();
} catch (FlyingPig e) {
    ...
} catch (Swine e) {
    ...
}
于 2010-05-03T03:35:34.627 に答える
2

・プレイ!フレームワークは、リクエスト処理にこのようなものを使用します。リクエスト処理は多くのレイヤー (ルーティング、ミドルウェア、コントローラー、テンプレート レンダリング) を通過し、最終レイヤーでレンダリングされた HTML がスロー可能なものにラップされてスローされ、最上位のレイヤーがそれをキャッチしてラップを解除し、クライアントに送信します。したがって、関連する多くのレイヤーのメソッドはいずれも、応答オブジェクトを明示的に返す必要はありません。また、応答オブジェクトを引数として渡して伝達および変更する必要もありません。

詳細については少し大雑把です。Play! のコードを見ることができます。詳細については、フレームワークを参照してください。

于 2010-08-30T13:26:43.893 に答える