114

我担心这是一个运行时异常,所以应该谨慎使用它。
标准用例:

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. 似乎不应该使用它,我们应该坚持我们自己的检查异常。抛出这个的好用例是什么?

4

6 回答 6

88

API 文档IllegalArgumentException

抛出以指示方法已传递了非法或不适当的参数。

通过查看它在 JDK 库中的使用方式,我会说:

  • 在输入进入工作之前抱怨明显错误的输入似乎是一种防御措施,并导致某些事情在中途失败并显示无意义的错误消息。

  • 它用于抛出受检异常太烦人的情况(尽管它出现在 java.lang.reflect 代码中,在这种情况下,对抛出受检异常的荒谬程度的担忧并不明显)。

我会使用IllegalArgumentException对常用实用程序进行最后的防御性参数检查(尝试与 JDK 使用保持一致)。或者期望错误参数是程序员错误,类似于NullPointerException. 我不会用它来实现业务代码中的验证。我当然不会将它用于电子邮件示例。

于 2013-03-04T19:48:30.550 回答
26

在谈论“错误输入”时,您应该考虑输入的来源。

输入是由用户或您无法控制的其他外部系统输入的,您应该期望输入无效,并始终对其进行验证。在这种情况下抛出一个检查异常是完全可以的。您的应用程序应该通过向用户提供错误消息来从该异常中“恢复”。

如果输入来自您自己的系统,例如您的数据库或应用程序的某些其他部分,您应该能够相信它是有效的(它应该在到达那里之前已经过验证)。在这种情况下,抛出一个像 IllegalArgumentException 这样的未经检查的异常是完全可以的,它不应该被捕获(通常你​​不应该捕获未经检查的异常)。无效值首先到达那里是程序员的错误;)您需要修复它。

于 2013-03-04T20:06:14.547 回答
17

“谨慎地”抛出运行时异常并不是一个好策略——Effective Java 建议您在可以合理地预期调用者恢复时使用检查异常。(程序员错误是一个具体的例子:如果一个特殊情况表明程序员错误,那么你应该抛出一个未经检查的异常;你希望程序员有一个逻辑问题发生在哪里的堆栈跟踪,而不是试图自己处理。)

如果没有恢复的希望,请随意使用未经检查的异常;抓住它们是没有意义的,所以这很好。

但是,从您的示例中无法 100% 清楚地了解此示例在您的代码中的哪种情况。

于 2013-03-04T18:40:07.197 回答
6

如 oracle 官方教程中所述,它指出:

如果可以合理地期望客户端从异常中恢复,则使其成为受检异常。如果客户端无法从异常中恢复,请将其设为未经检查的异常。

如果我有一个使用 , 与数据库交互的应用程序JDBC,并且我有一个将参数作为int itemand的方法double price。对应的price项目是从数据库表中读取的。item我只是将购买的总数乘以price价值并返回结果。虽然我总是确定在我的最后(应用程序端),表中的价格字段值永远不会是负数。但是如果价格值出现负数怎么办?这表明数据库端存在严重问题。可能是运营商输入错误的价格。这是调用该方法的应用程序的另一部分无法预料也无法从中恢复的问题。它BUG在您的数据库中。所以 和IllegalArguementException()在这种情况下应该抛出the price can't be negative.
我希望我已经清楚地表达了我的观点..

于 2013-03-04T19:06:19.187 回答
6

任何 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% 的错误,因为它要求执行不可能的操作,因此最终它们是应该使应用程序崩溃的错误(因此这是一个不可恢复的错误)。

在这种情况下并遵循快速失败的方法,您应该让应用程序完成以避免破坏应用程序状态。

于 2015-12-17T11:47:12.703 回答
6

视为前提条件IllegalArgumentException检查,并考虑设计原则:公共方法应该知道并公开记录自己的前提条件。

我同意这个例子是正确的:

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

如果 EmailUtil 是不透明的,这意味着无法向最终用户描述先决条件是有原因的,那么检查的异常是正确的。第二个版本,更正了这个设计:

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在客户端调用无法知道对象的状态不一致的情况下,最终用户无法看到私有状态。当它优于检查异常时,我没有一个很好的解释,尽管诸如初始化两次或丢失未恢复的数据库连接之类的例子都是示例。

于 2018-03-27T16:15:00.463 回答