我在Java中以标准方式使用断言,在我的IDE中打开它们。所以他们不是生产发布的一部分。最近我看到了throw new AssertionError()
的代码示例,我开始考虑应该使用AssertionError
而不是断言的情况。
我的猜测主要区别在于断言的可选性,因此它们不会降低生产性能,因此它们可以在代码中经常出现,但修复用户报告的难以重现的错误更难。
对于AssertionError
,恰恰相反。
我还发现AssertionError
在执行不应该获取的代码中更实用,而不是使用assert false //We should not be here
。特别是如果需要返回值。例如:
int getFoo(AnEnum a){
if (a == AnEnum.ONE)
return bar();
else if (a == AnEnum.TWO)
return SOME_VALUE;
//else
assert false; //throw new AssertionError();
return -1; //not necessary when usin AssertionError
}
AssertionError
中提供说明 - 是否应该提供或仅仅是Error
(以及
断言类型)足以或多或少地确定堆栈跟踪
如果发现错误,将提供?答案 0 :(得分:6)
在技术说明"Programming With Assertions: Control Flow Invariants"中,给出了以下代码:
void foo() {
for (...) {
if (...)
return;
}
assert false; // Execution should never reach this point!
}
但是也给出了以下注释:
注意:谨慎使用此技术。如果Java语言规范中定义的语句无法访问,如果您尝试断言未到达,则会出现编译时错误。同样,一个可接受的替代方案就是抛出AssertionError。
断言断言时,您可能不会指望抛出AssertionError
。由于AssertionError
构造函数是公开的,并且由于可能没有替换AssertionError(String message, Throwable cause)
,我猜你应该期望它们即使它们被关闭。
在无法访问的代码上抛出AssertionError
(即没有任何要评估的真实表达式)将永远不会像Jon Skeet建议的那样减慢代码速度,因此在性能方面不会受到影响。
所以最后抛出AssertionError
似乎没问题。
答案 1 :(得分:6)
我建议不要直接投掷AssertionError
。如果您选择依靠AssertionError
来检查不变量,前/后条件,状态条件等,那么在生产中打开“-ea”标志的常规断言仍然会更好。<登记/>
原因是断言机制(除了在编译器级别进行优化)使您有机会立即打开或关闭所有断言。即使您现在无法想到这样做的理由,如果您在将来遇到某个原因,只需考虑您必须查看所有throw new AssertionError(...)
类型代码并将其包围起来if
条款。你得到了照片
正如您不希望在代码中的许多位置硬编码的幻数,并且可能会使用常量,您不应该感染您的代码有很多重复(即throw new AssertionError(...)
部分)。
关于断言的另一个词。我相信在依赖生产代码中的断言错误之前,您应该三思而后行。原因是AssertionError
非常通用。它有一个信息和一个原因,但这就是它
相反,请考虑使用特定的RuntimeException
子类,这些子类将通过与问题更相关的特定类以及携带与问题相关的实际数据来传达更多信息。
举个简单的例子,考虑一下您在问题中提到的案例,其中有一部分代码是您不希望达到的。断言或AssertionError
会传达一个事实,即你达到了一些意想不到的代码,但不是更多。使用特定的RuntimeException
还可以在该时间点传递方法的局部变量和参数的状态。您可以认为这可以通过设置断言消息或AssertionError
来包含此信息来实现,但在使用自动错误记录/处理机制时这不起作用。这些机制可以使用访问者模式来处理意外行为,这些访问者模式是用于检查意外行为的RuntimeException
的不同子类(通过句柄我也意味着快速失败,不一定是恢复)。