题
我想知道为什么 assert
Java 中关键字的使用率这么低?我几乎从未见过它们被使用过,但我认为它们是个好主意。我当然更喜欢简洁:
assert param != null : "Param cannot be null";
冗长的:
if (param == null) {
throw new IllegalArgumentException("Param cannot be null");
}
我怀疑它们没有得到充分利用,因为
- 他们来得比较晚(Java 1.4),那时很多人已经建立了他们的Java编程风格/习惯
- 默认情况下它们在运行时关闭
解决方案
断言 理论上是用于测试 不变量, ,假设 必须 为 true 以便代码正确完成。
显示的示例是测试有效输入,这不是断言的典型用法,因为它通常是用户提供的。
断言通常不在生产代码中使用,因为存在开销,并且假设不变量失败的情况在开发和测试期间已被捕获为编码错误。
您关于它们在 Java 中“迟到”的观点也是它们没有被更广泛看到的原因。
此外,单元测试框架允许将编程断言的某些需求置于正在测试的代码之外。
其他提示
使用断言来测试用户输入是对断言的滥用。扔一个 IllegalArgumentException
在无效输入上更正确,因为它允许调用方法捕获异常,显示错误,并执行所需的任何操作(再次请求输入,退出,等等)。
如果该方法是您的某个类中的私有方法,则断言很好,因为您只是想确保不会意外地向其传递空参数。您可以在打开断言的情况下进行测试,当您测试了所有路径并且未触发断言时,您可以将它们关闭,这样就不会在它们上浪费资源。它们作为注释也很有用。一个 assert
在一个方法的开始是对维护者来说他们应该遵循某些先决条件的良好文档,以及 assert
最后用后置条件记录该方法应该做什么。它们和评论一样有用;此外,因为有了断言,他们实际上测试了他们记录的内容。
断言用于测试/调试,而不是错误检查,这就是它们默认关闭的原因:阻止人们使用断言来验证用户输入。
从 使用断言编程
默认情况下,断言在运行时被禁用。两个命令行开关允许您有选择地启用或禁用断言。
这意味着,如果您不能完全控制运行时环境,则无法保证断言代码会被调用。断言适用于测试环境,而不是生产代码。您不能用断言替换异常处理,因为如果用户在禁用断言的情况下运行您的应用程序( 默认),所有错误处理代码都会消失。
在“Effective Java”中,Joshua Bloch 建议(在“检查参数有效性”主题中)(有点像采用的简单规则),对于公共方法,我们应验证参数并在发现无效时抛出必要的异常,并且对于非公共方法(不公开,您作为它们的用户应该确保其有效性),我们可以使用断言来代替。
yc
@Don,您对断言默认关闭感到沮丧。我也是,因此编写了这个小 javac 插件来内联它们(即发出字节码 if (!expr) throw Ex
而不是这个愚蠢的断言字节码。
如果你包括 法贾尔 在编译 Java 代码时在你的类路径中,它会发挥它的魔力,然后告诉
Note: %n assertions inlined.
@看 http://smallwiki.unibe.ch/adriankuhn/javacompiler/forceassertions 或者在 github 上 https://github.com/akuhn/javac
我不知道为什么你会费心编写断言,然后用标准的 if then 条件语句替换它们,为什么不首先将条件写成 ifs 呢?
断言仅用于测试,它们有两个副作用:启用后,二进制文件会变大,性能会下降(这就是您可以将其关闭的原因!)
断言不应用于验证条件,因为这意味着启用/禁用断言时应用程序的行为在运行时会有所不同 - 这是一场噩梦!
断言很有用,因为它们:
- 及早发现编程错误
- 使用代码记录代码
将它们视为代码自我验证。如果它们失败,则意味着您的程序已损坏并且必须停止。在单元测试时始终打开它们!
在 务实的程序员 他们甚至建议让它们在生产中运行。
启用断言
使用断言来防止不可能的事情发生。
请注意,如果断言失败,则会抛出 AssertionError,因此不会被 catch Exception 捕获。
断言非常有限:您只能测试布尔条件,并且每次都需要编写有用的错误消息的代码。将此与 JUnit 的assertEquals() 进行比较,后者允许从输入生成有用的错误消息,甚至可以在 JUnit 运行程序的 IDE 中并排显示两个输入。
另外,到目前为止,您无法在我见过的任何 IDE 中搜索断言,但每个 IDE 都可以搜索方法调用。
事实上,它们是在 Java 1.4 中出现的
我认为主要问题是,当您在自己不直接管理jvm选项的环境中编码时,例如在eclipse或J2EE服务器中(在这两种情况下都可以更改jvm选项,但您需要深入搜索以找到它在哪里)可以完成),使用 if 和异常(或者更糟糕的是不使用任何东西)更容易(我的意思是它需要更少的努力)。
正如其他人所说:断言不适合验证用户输入。
如果您担心冗长,我建议您查看我编写的库: https://bitbucket.org/cowwoc/requirements/. 。它将允许您使用很少的代码来表达这些检查,它甚至会代表您生成错误消息:
requireThat("name", value).isNotNull();
如果你坚持使用断言,你也可以这样做:
assertThat("name", value).isNotNull();
输出将如下所示:
java.lang.NullPointerException: name may not be null
太长了;博士
本页上的大多数其他答案都强调“断言通常不会在生产代码中使用”这一格言。虽然在文字处理程序或电子表格等生产力应用程序中确实如此,但在 Java 如此普遍使用的自定义业务应用程序中,生产中的断言测试非常有用且常见。
就像编程世界中的许多格言一样,在一种情况下开始正确的东西会被误解,然后在其他情况下被误用。
生产力应用程序
“断言通常不会在生产代码中使用”这句格言虽然很常见,但却是不正确的。
形式化断言测试起源于诸如文字处理器之类的应用程序 微软Word 或类似的电子表格 微软Excel. 。这些应用程序可能会调用一系列断言测试断言 每次击键时 由用户制作。这样的 极端 重复严重影响了性能。因此,只有有限分发的此类产品的测试版启用了断言。因此格言。
商业应用程序
相比之下,在用于数据输入、数据库或其他数据处理的面向业务的应用程序中,在生产中使用断言测试非常有用。对性能的影响微不足道,因此非常实用且常见。
测试业务规则
在生产运行时验证业务规则是完全合理的,应该鼓励。例如,如果发票必须始终有一个或多个行项目,则编写一个断言测试发票行项目的计数是否大于零。如果产品名称必须至少包含 3 个字符或更多,请编写一个断言来测试字符串的长度。此类测试对生产性能没有重大影响。
运行时条件
如果您的应用程序期望在生产环境中运行时某些条件始终为真,请将这些期望作为断言测试写入您的代码中。
如果您预计这些条件有时可能会失败,那么请执行以下操作 不是 编写断言测试。也许抛出某些异常。然后尽可能尝试恢复。
健全性检查
健全性检查 在生产运行时也是完全合理的,应该鼓励。当一些奇怪的事情发生时,测试一些人们无法想象是不真实的任意条件已经在无数的情况下拯救了我的培根。
例如,测试将某个库中的五分钱 (0.05) 四舍五入到便士会导致五分钱 (0.05),这帮助我成为第一批发现 Apple 在其产品中提供的浮点技术缺陷的人之一。 罗塞塔 PowerPC 到 Intel 过渡期间的库。这种缺陷似乎不可能出现在公众面前。但令人惊讶的是,这个缺陷没有引起原始供应商 Transitive 和 Apple 以及在 Apple 测试版上进行测试的早期开发者的注意。
(顺便说一句,我应该提到......永远不要使用 货币的浮点数, , 使用 BigDecimal
.)
框架的选择
而不是使用内置的 assert
设施,您可能需要考虑使用另一个断言框架。您有多种选择,包括:
- 联合单元
看:org.junit.jupiter.api.Assertions
. - 断言J
以其光滑而闻名 流畅的界面. - 汉克雷斯特
可用于多种语言(Java、Python、Ruby、Swift 等)。
或者自己动手。创建一个小类以在您的项目中使用。像这样的东西。
package work.basil.example;
public class Assertions {
static public void assertTrue ( Boolean booleanExpression , CharSequence message ) throws java.lang.AssertionError {
if ( booleanExpression ) {
// No code needed here.
} else { // If booleanExpression is false rather than expected true, throw assertion error.
// FIXME: Add logging.
throw new java.lang.AssertionError( message.toString() );
}
}
}
用法示例:
Assertions.assertTrue(
localTime.isBefore( LocalTime.NOON ) ,
"The time-of-day is too late, after noon: " + localTime + ". Message # 816a2a26-2b95-45fa-9b0a-5d10884d819d."
) ;
你的问题
他们来得比较晚(Java 1.4),那时很多人已经建立了他们的Java编程风格/习惯
是的,这是千真万确的。许多人对 Sun/JCP 为断言测试开发的 API 感到失望。与现有的图书馆相比,它的设计乏善可陈。很多人忽略了新的 API,而坚持使用已知的工具(第 3 方工具或自己的迷你库)。
它们默认在运行时关闭,为什么哦为什么?
在最早的几年里,Java 因性能速度不佳而受到诟病。讽刺地, Java 快速发展 成为最佳表现平台之一。但坏名声却像臭味一样挥之不去。因此,Sun 对于任何可能以任何可衡量的方式影响性能的事情都非常警惕。因此从这个角度来看,将禁用断言测试设置为默认值是有意义的。
默认情况下禁用的另一个原因可能与以下事实有关:在添加新的断言工具时,Sun 劫持了该词 assert
. 。这是 不是 以前保留的关键字,并且需要对 Java 语言进行的少数更改之一。方法名称 assert
已被许多库和许多开发人员在自己的代码中使用。有关这一历史转变的一些讨论,请阅读这个旧文档, 使用断言编程.