在java中使用AssertionError和断言

我以标准方式在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 (和断言类型)的事实足以或多或少地确定在发现错误的情况下将提供堆栈跟踪?

在技​​术说明“使用断言编程:控制流不变量”中 ,给出了以下代码:

 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似乎没问题。

我建议不要直接抛出AssertionError 。 如果您选择依赖AssertionError来检查不变量,前/后条件,状态条件等,那么在生产中打开“-ea”标志的常规断言仍然会更好。
原因是断言机制(除了在编译器级别进行优化)使您有机会立即打开或关闭所有断言。 即使您现在无法想到这样做的理由,如果您在将来遇到某个原因,只需考虑您将不得不重新考虑所有throw new AssertionError(...)类型代码并将其包围起来有一个讨厌的if子句。 你得到了照片。
正如您不希望在代码中的许多位置硬编码的幻数,并且可能会使用常量,您不应该感染您的代码有很多重复(即throw new AssertionError(...)部分) 。

关于断言的另一个词虽然。 我相信你应该在依赖生产代码中的断言错误之前再三考虑。 原因是AssertionError非常通用。 它有一个信息和一个原因,但这就是它。
相反,请考虑使用特定的RuntimeException子类,这些子类将通过与问题更相关的特定类以及携带与问题相关的实际数据来传达更多信息。
举个简单的例子,考虑一下您在问题中提到的案例,其中有一部分代码是您不希望达到的。 断言或AssertionError会传达一个事实,即你达到了一些意想不到的代码,但不是更多。 使用特定的RuntimeException还可以在该时间点提供方法的局部变量和参数的状态。 您可以认为这可以通过设置断言或AssertionError的消息来包含此信息,但在使用自动错误记录/处理机制时这不起作用。 这些机制可以使用访问者模式处理意外行为,这些访问者模式是用于检查意外行为的RuntimeException的不同子类(通过句柄我也意味着快速失败,不一定是恢复)。