什么时候应该抛出 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一致的政策。它似乎不应该被使用,我们应该坚持我们自己检查的异常。抛出这个问题的好用例是什么?

238522 次浏览

“谨慎地”抛出运行时异常并不是一个好策略——有效的 Java 建议在 the caller can reasonably be expected to recover时使用检查异常。(程序员错误是一个特定的例子: 如果一个特定的情况表明程序员错误,那么你应该抛出一个未检查的异常; 你希望程序员有一个堆栈跟踪逻辑问题发生的地方,而不是尝试自己处理它

If there's no hope of recovery, then feel free to use unchecked exceptions; there's no point in catching them, so that's perfectly fine.

但是,从您的示例中并不能100% 清楚地看出这个示例在您的代码中是哪种情况。

正如甲骨文官方教程中所规定的,它指出:

如果可以合理地期望客户端从异常中恢复, make it a checked exception. If a client cannot do anything to recover 从异常中选择一个未检查的异常。 < br >

If I have an Application interacting with database using JDBC , And I have a method that takes the argument as the int item and double price. The price for corresponding item is read from database table. I simply multiply the total number of item purchased with the price value and return the result. Although I am always sure at my end(Application end) that price field value in the table could never be negative .But what if the price value comes out 没有? It shows that there is a serious issue with the database side. Perhaps wrong price entry by the operator. This is the kind of issue that the other part of application calling that method can't anticipate and can't recover from it. It is a BUG in your database. So , and IllegalArguementException() should be thrown in this case which would state that the price can't be negative.
我希望我已经清楚地表达了我的观点. 。

IllegalArgumentException的 API 文档:

引发以指示传递了非法或不适当的参数的方法。

如何在 JDK 库中使用它的角度来看,我会说:

  • It seems like a defensive measure to complain about obviously bad input before the input can get into the works and cause something to fail halfway through with a nonsensical error message.

  • 它用于引发检查异常非常烦人的情况(尽管它出现在 java.lang.response 代码中,在这种情况下,对可笑级别的检查异常引发的担忧并不明显)。

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

当谈到“坏的输入”时,你应该考虑输入来自哪里。

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

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

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

In this case and following the approach of fail fast you should let the application finish to avoid corrupting the application state.

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);
}

If EmailUtil is transparent, for instance maybe it's a private method owned by the class under question, IllegalArgumentException is correct if and only if its preconditions can be described in the function documentation. This is a correct version as well:

/** @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。当它优于检查异常时,我没有一个很好的解释,尽管例如初始化两次,或者丢失一个未恢复的数据库连接。