我担心这是一个运行时异常,所以应该谨慎使用。
标准用例:
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
。似乎不应该使用它,我们应该坚持使用自己的受控异常。抛出这个的好用例是什么?
IllegalArgumentException
API文档:
抛出,指示方法被传递了非法或不适当的参数。
从它在JDK库中的使用来看,我想说:
-
这似乎是一种防御措施,在输入进入工作之前抱怨明显错误的输入,并导致某些内容在中途失败并出现无意义的错误消息。
-
它用于抛出检查异常太烦人的情况(尽管它在java.lang.reflect代码中出现,在那里对荒谬的检查异常抛出级别的关注并不明显)。
我将使用IllegalArgumentException
对常用实用程序进行最后的防御性参数检查(试图与JDK的使用保持一致)。或者期望一个糟糕的参数是程序员的错误,类似于NullPointerException
。我不会用它来实现业务代码中的验证。我当然不会在电子邮件的例子中使用它。
在讨论"不良输入"时,您应该考虑输入的来源。
是由用户或您无法控制的另一个外部系统输入的输入,您应该期望该输入是无效的,并始终对其进行验证。在这种情况下,抛出一个检查异常是完全可以的。您的应用程序应该通过向用户提供错误消息来"恢复"此异常。
如果输入来自你自己的系统,例如你的数据库,或者你的应用程序的其他部分,你应该能够依赖它是有效的(它应该在到达那里之前被验证过)。在这种情况下,抛出像IllegalArgumentException这样的未检查异常是完全可以的,它不应该被捕获(一般情况下,您永远不应该捕获未检查异常)。这是一个程序员的错误,无效的值在那里首先;)你得把它修好。
"有节制地"抛出运行时异常并不是一个好的策略——Effective Java建议当调用者可以合理地期望恢复时使用受控异常。(程序员错误是一个具体的例子:如果一个特定的情况表明程序员错误,那么你应该抛出一个未检查的异常;您希望程序员对逻辑问题发生的位置有堆栈跟踪,而不是试图自己处理它。
如果没有恢复的希望,那么可以随意使用未检查的异常;抓它们没有意义,所以这完全没问题。
从你的例子中并不是100%清楚这个例子是在你的代码中,尽管
将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
。我不能很好地解释为什么它比受控异常更受欢迎,尽管诸如初始化两次或丢失无法恢复的数据库连接之类的事情都是例子。
oracle官方教程中规定:
如果客户端可以合理地从异常中恢复,使其成为一个检查异常。如果客户端无法恢复从异常中,使其为未检查异常。
如果我有一个应用程序使用JDBC
与数据库交互,并且我有一个方法将参数作为int item
和double price
。从数据库表中读取对应项的price
。我只需将购买的item
的总数与price
的值相乘,然后返回结果。虽然我总是确定在我的端(应用程序端)表中的价格字段值永远不会是负的,但是如果价格值为负怎么办?这表明数据库端存在严重的问题。可能是操作员输入错误的价格。这是一种调用该方法的应用程序的其他部分无法预测也无法从中恢复的问题。它是数据库中的BUG
。因此,在本例中应该抛出IllegalArguementException()
,这将表明the price can't be negative
。
我希望我已经清楚地表达了我的观点。
任何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%的错误,因为它要求不可能的操作,所以最终它们是应该使应用程序崩溃的错误(所以这是一个不可恢复的错误)。
在这种情况下,遵循快速失败的方法,您应该让应用程序结束,以避免破坏应用程序状态。