应该何时抛出IllegalArgumentException?

dje*_*lin 94 java exception illegalargumentexception

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

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

但这似乎会迫使以下设计:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }
Run Code Online (Sandbox Code Playgroud)

让它回到被检查的例外.

好的,但让我们继续吧.如果输入错误,则会出现运行时错误.首先,这实际上是统一实施的相当困难的政策,因为您可能必须进行相反的转换:

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

更糟糕的是 - 虽然检查0 <= pct && pct <= 100客户端代码可能需要静态执行,但对于更高级的数据(如电子邮件地址)则更是如此,或者更糟糕的是,必须针对数据库进行检查,因此通常客户端代码无法预先验证.

所以基本上我所说的是我没有看到一个有意义的一致政策使用IllegalArgumentException.它似乎不应该被使用,我们应该坚持我们自己检查的例外.扔这个的好用例是什么?

Nat*_*hes 77

IllegalArgumentException的api文档是:

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

从查看如何在jdk库中使用它,我会说:

  • 在输入进入工作之前抱怨显然输入错误似乎是一种防御性措施,并导致某些事情在失败的情况下失败并出现无意义的错误消息.

  • 它用于抛出一个经过检查的异常会很烦人的情况(尽管它在java.lang.reflect代码中出现,其中关于可疑异常抛出的荒谬级别的关注并不明显).

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

  • 我认为"期望是一个坏的论点是程序员错误的地方"的建议与我看到这个使用的方式最为一致,所以接受这个答案. (8认同)

Tom*_*Tom 20

在谈论"输入错误"时,您应该考虑输入的来源.

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

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

  • 因为编程错误会导致未经检查的异常被抛出.抛出此类异常的方法的调用者无法合理地期望从中恢复,因此捕获它们通常没有意义. (8认同)
  • 为什么“你永远不应该捕捉未经检查的异常”? (3认同)
  • `因为一个未经检查的异常意味着由于编程错误而被抛出`帮助我清除了我脑海中的很多东西,谢谢:) (3认同)

Lou*_*man 13

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

如果没有恢复的希望,那么随意使用未经检查的例外; 抓住他们是没有意义的,所以这很好.

但是,从您的示例中可以看出这个示例在您的代码中的情况并非100%明确.

  • 在核冷却应用中,我宁愿在测试中努力工作,而不是允许程序员认为不可能忽视的情况. (4认同)

dje*_*lin 8

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

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

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

如果 EmailUtil 是不透明的,这意味着由于某种原因无法向最终用户描述先决条件,则已检查的异常是正确的。第二个版本,针对此设计进行了更正:

import com.someoneelse.EmailUtil;

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

如果 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");
  }
}
Run Code Online (Sandbox Code Playgroud)

这种设计可以采用任何一种方式。

  • 如果描述先决条件的成本很高,或者如果该类旨在供不知道其电子邮件是否有效的客户使用,则使用ParseException。这里的顶级方法被命名scanEmail为暗示最终用户打算发送未经研究的电子邮件,因此这可能是正确的。
  • 如果可以在函数文档中描述前提条件,并且该类无意进行无效输入,因此指示程序员错误,请使用IllegalArgumentException. 尽管没有“检查”,但“检查”会转移到记录该函数的 Javadoc,客户端应遵守该功能。IllegalArgumentException如果客户事先无法判断他们的论点是非法的,那就是错误的。

关于 IllegalStateException 的注释:这意味着“此对象的内部状态(私有实例变量)无法执行此操作。” 最终用户无法看到私有状态,因此宽松地说,IllegalArgumentException在客户端调用无法知道对象状态不一致的情况下,它优先于私有状态。我没有很好的解释什么时候它比检查异常更受欢迎,尽管初始化两次或丢失未恢复的数据库连接等都是例子。


Vis*_*l K 5

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

如果可以合理预期客户端会从异常中恢复,请将其设置为已检查的异常。如果客户端无法采取任何措施来从异常中恢复,请将其设置为未经检查的异常。

如果我有一个使用进行数据库交互的应用程序JDBC,并且我有一个将参数作为int itemand的方法double price。在price相应项目从数据库表中读取。我只需将item购买的总数乘以该price值,然后返回结果。尽管我始终确保在应用程序末尾该表中的价格字段值永远不会为负。但是,如果价格值出现负数怎么办?它表明数据库方面存在严重问题。运营商可能输入了错误的价格。这是应用程序的其他部分调用该方法无法预期并且无法从中恢复的问题。它BUG在您的数据库中。所以IllegalArguementException()在这种情况下应该抛出这样的声明the price can't be negative
我希望我已经明确表达了我的观点。


Ign*_*cia 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

它们代表应用程序中99.9%的错误,因为它要求不可能的操作,所以最终它们是应该使应用程序崩溃的错误(因此它是一个不可恢复的错误).

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

  • 当然,它不应该使您的 API 服务器崩溃,而是向调用方返回一个异常,该异常不应使客户端崩溃。 (2认同)