我想知道为什么assert
关键字在Java中未得到充分利用?我几乎从未见过它们,但我认为它们是个好主意。我当然更喜欢简洁:
assert param != null : "Param cannot be null";
详细说明:
if (param == null) {
throw new IllegalArgumentException("Param cannot be null");
}
我怀疑他们未得到充分利用,因为
答案 0 :(得分:62)
断言用于测试invariants,假设必须为真,以便代码正确完成。
显示的示例是测试有效输入,这不是断言的典型用法,因为它通常是用户提供的。
断言通常不会在生产代码中使用,因为存在开销,并且假设不变量失败的情况在开发和测试期间被捕获为编码错误。
关于他们对Java“迟到”的观点也是他们没有被广泛看到的原因。
此外,单元测试框架允许程序化断言的某些需要在被测试代码的外部。
答案 1 :(得分:56)
滥用断言来使用它们来测试用户输入。在无效输入上抛出IllegalArgumentException
更正确,因为它允许调用方法捕获异常,显示错误,并做任何需要(再次请求输入,退出,等等)。
如果该方法是其中一个类中的私有方法,则断言很好,因为您只是想确保不会意外地将它传递给null参数。您可以使用断言进行测试,当您测试了所有路径并且未触发断言时,您可以将其关闭,这样您就不会浪费资源。它们也像注释一样有用。方法开头的assert
是维护者的良好文档,他们应该遵循某些先决条件,并且最后使用后置条件的assert
记录该方法应该做什么。它们和评论一样有用;此外,因为断言,他们实际上测试他们记录的内容。
断言用于测试/调试,而不是错误检查,这就是为什么它们默认是关闭的:阻止人们使用断言来验证用户输入。
答案 2 :(得分:19)
默认情况下,在运行时禁用断言。两个命令行开关允许您有选择地启用或禁用断言。
这意味着如果您无法完全控制运行时环境,则无法保证甚至可以调用断言代码。断言意味着在测试环境中使用,而不是用于生产代码。您无法使用断言替换异常处理,因为如果用户在禁用断言的情况下运行您的应用程序(默认),则所有错误处理代码都会消失。
答案 3 :(得分:18)
在“Effective Java”中,Joshua Bloch建议(在“检查参数的有效性”主题中)(有点像一个简单的规则),对于公共方法,我们将验证参数并抛出必要的异常,如果找到无效,对于非公开方法(没有公开,你作为他们的用户应该确保它们的有效性),我们可以使用断言。
YC
答案 4 :(得分:9)
@Don,你很沮丧默认情况下关闭断言。我也是,因此编写了这个内联它的小javac插件(即发出if (!expr) throw Ex
的字节码而不是这个愚蠢的断言字节码。
如果在编译Java代码时在类路径中包含fa.jar,它将发挥其魔力然后告诉
Note: %n assertions inlined.
@see http://smallwiki.unibe.ch/adriankuhn/javacompiler/forceassertions以及github https://github.com/akuhn/javac
答案 5 :(得分:2)
我不确定为什么你会费心去编写断言,然后用标准if条件语句替换它们,为什么不首先把条件写成ifs?
断言仅用于测试,它们有两个副作用:启用时更大的二进制文件和降低的性能(这就是为什么你可以关闭它们!)
断言不应该用于验证条件,因为这意味着当启用/禁用断言时,应用程序的行为在运行时会有所不同 - 这是一场噩梦!
答案 6 :(得分:2)
断言很有用,因为它们:
将它们视为代码自我验证。如果他们失败了,那就意味着你的程序被破坏了,必须停止。在单元测试时始终打开它们!
在The Pragmatic Programmer中,他们甚至建议让他们投入生产。
让断言开启
使用断言防止不可能。
请注意,如果断言失败,则断言会抛出AssertionError,因此不会被catch异常捕获。
答案 7 :(得分:1)
断言非常有限:您只能测试布尔条件,并且每次都需要编写有用的错误消息的代码。将此与JUnit的assertEquals()进行比较,它允许从输入生成有用的错误消息,甚至在JUnit运行器中的IDE中并排显示两个输入。
此外,您无法在我看到的任何IDE中搜索断言,但每个IDE都可以搜索方法调用。
答案 8 :(得分:0)
实际上他们来到了Java 1.4
我认为主要的问题是当你在一个你不直接管理jvm选项的环境中进行编码时,就像在eclipse或J2EE服务器中一样(在这两种情况下都可以更改jvm选项,但你需要深入搜索到找到它可以做到的地方),使用if和exception(或者更糟的是不使用任何东西)更容易(我的意思是它需要更少的努力)。
答案 9 :(得分:0)
正如其他人所说:断言不适合验证用户输入。
如果您关注详细程度,我建议您查看我写的图书馆:https://bitbucket.org/cowwoc/requirements/。它允许您使用非常少的代码表达这些检查,它甚至会代表您生成错误消息:
requireThat("name", value).isNotNull();
如果你坚持使用断言,你也可以这样做:
assertThat("name", value).isNotNull();
输出将如下所示:
java.lang.NullPointerException: name may not be null
答案 10 :(得分:0)
此页面上的其他大多数答案都遵循格言“断言在生产代码中通常不使用”。在生产力应用程序(如文字处理器或电子表格)中为true,而在自定义业务应用程序(其中Java如此常用)中为真,在生产中进行断言测试非常有用,而且很常见。
就像编程世界中的许多格言一样,在一种情况下最初是正确的,但在其他情况下却会误用。
“断言在生产代码中通常不使用”这一格言虽然很普遍,但却是不正确的。
正式的断言测试源自诸如Microsoft Word之类的文字处理器或Microsoft Excel之类的电子表格之类的应用。这些应用可能会在用户进行的每次按键操作 上调用一系列断言测试断言。这种 extreme 重复会严重影响性能。因此,只有在有限发行中的此类产品的beta版本才启用断言。因此,格言。
相比之下,在用于数据输入,数据库或其他数据处理的面向业务的应用程序中,在生产中使用断言测试非常有用。对性能的微不足道的影响使其非常实用,而且很常见。
在生产中的运行时验证业务规则是完全合理的,应该鼓励这样做。例如,如果发票必须始终有一个或多个订单项,则编写一个断言测试,以使发票订单项的计数大于零。如果产品名称必须至少包含3个字符,请编写一个断言来测试字符串的长度。此类测试对生产性能没有重大影响。
如果您的应用在生产中运行时期望某些条件始终为真,则将这些期望作为断言测试写入到您的代码中。
如果您希望这些条件有时可能会失败,请不编写断言测试。也许抛出某些例外。然后尝试尽可能恢复。
Sanity checks也是完全合理的,应予以鼓励。测试一些无法想象不真实的任意条件,使我的培根在数不胜数的情况下得救了,这发生了一些奇怪的事情。
例如,测试将一分钱的镍(0.05)四舍五入会导致某个库中的镍(0.05),这使我成为第一个发现Apple随附的浮点技术缺陷的人之一PowerPC到Intel过渡期间的Rosetta库。向公众传播这种缺陷似乎是不可能的。但令人惊讶的是,该漏洞没有引起原始厂商,Transitive和Apple以及早期访问开发人员在Apple Beta上进行测试的注意。
(顺便说一句,……永远不要使用floating-point for money,请使用BigDecimal
。)
您可能要考虑使用另一个断言框架,而不是使用内置的assert
工具。您有多种选择,包括:
org.junit.jupiter.api.Assertions
。或者自己动手。在您的项目中使用一个小类。像这样的东西。
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,而使用了已知的工具(第三方工具或自带的微型库)。
默认情况下,它们在运行时处于关闭状态,为什么呢?
在最早的几年中,Java因性能下降而受到了不满。具有讽刺意味的是,Java quickly evolved成为最佳的性能平台之一。但是糟糕的说唱却像臭味一样散落。因此,Sun非常警惕任何可能以任何可衡量的方式影响性能的事情。因此,从这个角度来看,禁用断言测试默认值是有意义的。
默认情况下禁用的另一个原因可能与以下事实有关:在添加新的断言功能时,Sun劫持了assert
一词。这不是以前保留的关键字,不是,并且需要对Java语言进行的少数更改之一。许多库和许多开发人员在自己的代码中都使用了方法名assert
。有关此历史过渡的一些讨论,请阅读此旧文档Programming With Assertions。