web-dev-qa-db-ja.com

IllegalArgumentExceptionはいつスローされますか?

これはランタイム例外であることが心配なので、おそらく慎重に使用する必要があります。
標準的な使用例:

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の使用に関する意味のある一貫したポリシーが表示されないということです。使用すべきではないようで、独自のチェック済み例外に固執する必要があります。これを投げるのに良いユースケースは何ですか?

89
djechlin

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 [email protected]
 * 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回初期化する、回復されないデータベース接続を失うなどが例です。

1
djechlin

IllegalArgumentExceptionのAPIドキュメントは次のとおりです。

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

jdkライブラリでどのように使用されるか を見てから、私は言うでしょう:

  • 入力が作業に入る前に明らかに悪い入力について文句を言い、無意味なエラーメッセージで途中で何かを失敗させる防御策のようです。

  • これは、チェック例外をスローするのが面倒な場合に使用されます(ただし、Java.lang.reflectコードに出現しますが、それ以外の場合、チェック例外例外のばかげたレベルに関する懸念は明らかではありません)。

IllegalArgumentExceptionを使用して、一般的なユーティリティ(jdkの使用法との一貫性を保とうとする)の最後のディフェンシブな引数チェックを行います。NPEと同様に、不正な引数はプログラマーエラーであると予想されます。ビジネスコードで検証を実装するために使用しません。私は確かに電子メールの例には使用しません。

71
Nathan Hughes

「不正な入力」について話すときは、入力元を検討する必要があります。

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

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

19
Tom

ランタイム例外を「わずかに」スローすることは、実際には良いポリシーではありません。有効なJavaは、呼び出し側が回復することが合理的に期待できるの場合にチェック例外を使用することを推奨します。 (プログラマーエラーは特定の例です。特定のケースがプログラマーエラーを示している場合は、未チェックの例外をスローする必要があります。プログラマーは、自分で処理しようとせずに、ロジックの問題が発生した場所のスタックトレースを取得する必要があります。)

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

ただし、この例がコードに含まれている場合は、100%明確ではありません。

13
Louis Wasserman

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%を表します。最終的にはアプリケーションをクラッシュさせるバグです(したがって、回復不能なエラーです)。

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

Oracle公式チュートリアルで指定されているように、次のように記述されています。

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

JDBCを使用してデータベースと対話するアプリケーションがあり、引数をint itemおよびdouble priceとしてとるメソッドがあります。対応するアイテムのpriceは、データベーステーブルから読み取られます。購入したitemの総数にpriceの値を掛けて、結果を返します。テーブルの価格フィールドの値が負の値になることは常にありませんが、価格の値が出る場合はどうすればいいですか負の?データベース側に重大な問題があることを示しています。おそらくオペレータによる間違った価格入力。これは、そのメソッドを呼び出すアプリケーションの他の部分が予測できず、回復できない問題です。データベース内のBUGです。したがって、この場合、the price can't be negativeを示すIllegalArguementException()をスローする必要があります。
自分の主張を明確に表明したことを願っています。

5
Vishal K