IllegalArgumentExceptionがスローされるのはいつですか?


98

これは実行時の例外であることが心配なので、控えめに使用する必要があります。
標準的な使用例:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

しかし、それは次の設計を強制するようです:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

チェック済みの例外に戻すには。

さて、それで行こう。不正な入力をすると、ランタイムエラーが発生します。したがって、最初にこれを実際に均一に実装するのはかなり難しいポリシーです。これは、正反対の変換を行わなければならない可能性があるためです。

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

さらに悪いことに0 <= pct && pct <= 100、クライアントコードのチェックは静的に行われることが期待できますが、電子メールアドレスなどのより高度なデータの場合はそうではなく、データベースに対してチェックする必要があるため、一般にクライアントコードは事前に実行できません。検証。

つまり、基本的に私が言っているのは、の使用に関して一貫した意味のあるポリシーが見当たらないということですIllegalArgumentException。使用すべきではないようで、独自のチェック済み例外に固執する必要があります。これをスローする良いユースケースは何ですか?

回答:


80

のAPIドキュメントIllegalArgumentException

メソッドに不正または不適切な引数が渡されたことを示すためにスローされます。

JDKライブラリどのように使用されているかを見ると、次のようになります。

  • 入力が機能し、無意味なエラーメッセージが表示されて途中でエラーが発生する前に、明らかに悪い入力について文句を言う防御策のようです。

  • これは、checked例外をスローするのが面倒である場合に使用されます(ただし、java.lang.reflectコードに表示されます。

私はIllegalArgumentException一般的なユーティリティの最後の溝の防御的な引数チェックを行うために使用します(JDKの使用法との整合性を保つために試みます)。または、期待に反するものとして、と同様に、不正な引数はプログラマエラーNullPointerExceptionです。ビジネスコードに検証を実装するためには使用しません。私は確かにそれを電子メールの例に使用しません。


8
「悪い引数はプログラマーのエラーであるという期待がどこにあるか」というアドバイスは、これがどのように使用されているかを私が見た方法と最も一致していると思うので、この答えを受け入れます。
djechlin 2013年

22

「悪い入力」について話すとき、入力がどこから来ているかを考慮する必要があります。

ユーザーまたはユーザーが制御していない別の外部システムによって入力された入力である場合、その入力は無効であると想定し、常に検証する必要があります。この場合、チェック例外をスローしても問題ありません。アプリケーションは、ユーザーにエラーメッセージを提供して、この例外から「回復」する必要があります。

入力がデータベースなどの独自のシステム、またはアプリケーションの他の部分からのものである場合、その入力が有効であることを信頼できるはずです(その前に検証されているはずです)。この場合、キャッチされるべきではないIllegalArgumentExceptionのようなチェックされていない例外をスローしても問題ありません(一般に、チェックされていない例外をキャッチすべきではありません)。無効な値がそもそも得られたのはプログラマのエラーです;)あなたはそれを修正する必要があります。


2
なぜ「チェックされていない例外をキャッチしてはならない」のですか?
Koray Tugay

9
未チェックの例外は、プログラミングエラーの結果としてスローされることを意図しているためです。このような例外をスローするメソッドの呼び出し元は、その例外から回復することを合理的に期待できないため、通常、それらをキャッチしても意味がありません。
トム

1
Because an unchecked exception is meant to be thrown as a result of a programming error私の頭の中の多くのものを取り除くのを手伝ってくれました、ありがとう:)
svarog

14

実行時例外を「惜しみなく」スローすることは、実際には適切なポリシーではありません。有効なJavaは、呼び出し元の回復が合理的に期待できる場合は、チェック例外を使用することをお勧めします。(プログラマーエラーは特定の例です。特定のケースがプログラマーエラーを示している場合は、チェックされていない例外をスローする必要があります。プログラマーがロジック問題が発生した場所のスタックトレースを取得し、自分で処理しようとしないでください。)

回復の見込みがない場合は、チェックされていない例外を使用してください。それらを捕まえる意味がないので、それで問題ありません。

ただし、この例がコード内にある場合は、例から100%明確ではありません。


「まあまあ回復する」とはおかしいと思います。一部のデータの形式が正しくない場合でも、呼び出し側が可能な限り多くの操作を成功させるために、一部の操作foo(data)が発生for(Data data : list) foo(data);した可能性があります。プログラムのエラーも含まれます。私のアプリケーションで障害が発生し、トランザクションが実行されないことを意味する場合、それはおそらく核冷却がオフラインになることを意味し、それはより良いことです。
djechlin 2013年

StackOverflowError呼び出し側が回復することを合理的に期待できない場合などです。しかし、データやアプリケーションロジックレベルのケースをチェックする必要があるようです。つまり、nullポインターチェックを実行します。
djechlin 2013年

4
核冷却アプリケーションでは、プログラマーが気付かずに合格することが不可能だと思ったケースを許可するよりも、テストで一生懸命失敗するほうがいいです。
Louis Wasserman 2013年

Boolean.parseBoolean(..)は、「呼び出し元の回復が合理的に期待できる」場合でもIllegalArugmentExceptionをスローします。そのため、コードを処理するか、呼び出し元にフェイルバックします。
ジェリルクック

5

オラクルの公式チュートリアルで指定されているように、それは次のように述べています:

クライアントが例外からの回復が合理的に期待できる場合は、チェック済み例外にします。クライアントが例外から回復するために何もできない場合は、チェックしない例外にします。

を使用してデータベースと対話するアプリケーションがある場合JDBCは、引数をint itemand として受け取るメソッドがありますdouble priceprice以下のために対応する項目は、データベーステーブルから読み出されます。item購入した合計数にprice値を掛けて結果を返します。テーブルの価格フィールドの値がマイナスになることはないと確信していますが、価格の値がマイナスになるとどうなりますか?データベース側に深刻な問題があることを示しています。おそらく、オペレーターによる誤った価格入力です。これは、そのメソッドを呼び出すアプリケーションの他の部分が予期できず、そこから回復できない種類の問題です。それはBUGあなたのデータベースにあります。だから、そしてIllegalArguementException()この場合は、それがスローされるはずですthe price can't be negative
私は私のポイントを明確に表現したことを望みます。


例外処理は回復するかどうかではなく回復する方法に関するものであるため、この(オラクルの)アドバイスは好きではありません。たとえば、1つの不正なユーザーリクエストは、Webサーバー全体をクラッシュさせる価値はありません。
djechlin

5

APIは、実行する前に、パブリックメソッドのすべてのパラメーターの有効性を確認する必要があります。

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

これらはアプリケーションでのエラーの99.9%を表しています。これは、不可能な操作を要求しているため、最終的にはアプリケーションをクラッシュさせるバグです(したがって、回復不可能なエラーです)。

この場合、フェイルファストのアプローチに従って、アプリケーションを終了させて​​、アプリケーションの状態の破損を回避する必要があります。


逆に、APIクライアントから不適切な入力があった場合、APIサーバー全体をクラッシュさせるべきではありません
djechlin 2017

2
もちろん、APIサーバーをクラッシュさせることはありませんが、呼び出し元に例外を返す必要があります。クライアント以外はクラッシュしないはずです。
イグナシオソレルガルシア

コメントに書いたのは、答えに書いたものではありません。
djechlin 2017年

1
間違ったパラメーター(バグ)を使用したAPIの呼び出しがサードパーティのクライアントによって行われた場合、クライアントはクラッシュするはずです。それがAPIサーバーであり、間違ったパラメーターでメソッドを呼び出すバグがある場合、APIサーバーはクラッシュするはずです。チェック:en.wikipedia.org/wiki/Fail-fast
Ignacio Soler Garcia

1

前提条件チェックIllegalArgumentExceptionとして扱い、設計原則を検討します。パブリックメソッドは、自身の前提条件を知っており、公に文書化する必要があります。

この例が正しいことに同意します。

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

EmailUtilが不透明である場合、つまり、前提条件をエンドユーザーに説明できない何らかの理由がある場合、チェックされた例外は正しいです。この設計用に修正された2番目のバージョン:

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

EmailUtilが透過的である場合、たとえば、問題のクラスが所有するプライベートメソッドであるIllegalArgumentException可能性がありますが、その前提条件を関数のドキュメントで説明できる場合にのみ適切です。これも正しいバージョンです:

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

このデザインはどちらにしてもよいでしょう。

  • 前提条件を説明するのにコストがかかる場合、またはメールが有効かどうかを知らないクライアントがクラスを使用することを目的としている場合は、を使用してくださいParseException。ここでのトップレベルのメソッドはscanEmail、エンドユーザーが調査されていない電子メールを送信するつもりであることを示す名前が付けられているため、これが正しい可能性があります。
  • 関数のドキュメントに前提条件を記述でき、クラスが無効な入力を意図していないためにプログラマーエラーが示される場合は、を使用しますIllegalArgumentException。「チェック」されていない場合でも、「チェック」はJavadocに移動し、クライアントが遵守することが期待される関数を文書化します。IllegalArgumentExceptionクライアントが彼らの議論が前もって違法であることを言うことができないところは間違っています。

IllegalStateExceptionに関する注意:これは、「このオブジェクトの内部状態(プライベートインスタンス変数)がこのアクションを実行できない」ことを意味します。エンドユーザーはプライベートな状態を見ることができないため、大まかに言えばIllegalArgumentException、クライアントの呼び出しでオブジェクトの状態が不整合であることがわからない場合に優先されます。チェックされた例外よりも優先される場合の良い説明はありませんが、2回の初期化や、回復されないデータベース接続の喪失などがその例です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.