Java声明断了吗?

在探讨问题时,我最近在Java中发现了assert关键字。 起初,我很兴奋。 我还不知道有用的东西! 一种更有效的方法来检查输入参数的有效性! 耶老师!

但后来我仔细研究了一下,我的热情并没有像一个简单的事实那样“完全被扼杀”而变得“脾气暴躁”:你可以关闭断言。*

这听起来像是一场噩梦。 如果我断言如果输入listOfStuffnull ,我不希望代码继续运行,为什么我想要忽略该断言呢? 听起来好像我正在调试一段生产代码并怀疑listOfStuff可能错误地传递了null但是没有看到任何触发该断言的日志文件证据,我不能相信listOfStuff实际上已经发送了一个有效的值; 我还必须考虑断言可能完全被关闭的可能性。

这假设我是调试代码的人。 不熟悉断言的人可能会看到并且(非常合理地)假设如果断言消息没有出现在日志中,则listOfStuff可能不是问题。 如果你第一次遇到assert是在野外,你甚至会发现它可以完全被关闭吗? 毕竟,它不像是一个允许你禁用try / catch块的命令行选项。

所有这些都让我想到了我的问题(这一个问题,而不是一个咆哮的借口!我保证!):

我错过了什么?

是否有一些细微差别使得Java的assert实现远比我给它的功劳更有用? 在某些情况下,从命令行启用/禁用它的能力实际上是非常有价值的吗? 当我设想在生产代码中使用它来代替if (listOfStuff == null) barf();等语句时,我是否误解了它if (listOfStuff == null) barf();

我觉得这里有一些重要的东西,我没有得到。

*好的,从技术上讲,它们实际上是默认关闭的; 你必须不遗余力地打开它们。 但是,你仍然可以完全击败他们。


编辑:启蒙要求,启示收到。

assert首先是一个调试工具的概念需要很长时间才能使它对我有意义。

我仍然认为应该在生产环境中禁用输入检查非平凡私有方法的概念,因为开发人员认为不可能输入错误。 根据我的经验,成熟的生产代码是一种疯狂的,庞大的东西,多年来由具有不同技能的人们开发,其目标是快速变化的不同程度的理智要求。 即使糟糕的输入确实是不可能的,从现在起六个月后的一段邋maintenance的维护编码也可以改变这一点。 提供的链接gustafc (谢谢!)包括这个例子:

assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;

禁用如此简单的生产检查让我感到愚蠢乐观。 然而,这是编码哲学的差异,而不是破碎的特征。

另外,我绝对可以看到这样的价值:

assert reallyExpensiveSanityCheck(someObject) : someObject;

感谢所有花时间帮助我理解这个function的人; 这是非常赞赏。

assert契约设计的有用部分。 在这种情况下,断言可用于:

  • 前提条件检查。
  • 后置条件检查。
  • 中间结果检查。
  • 类不变检查。

判断断言的代价可能很高(例如,在调用类的任何公共方法之前和之后必须保持类不变量)。 断言通常仅在调试版本中用于测试目的; 你断言那些不可能发生的事情 – 这些事情就是犯错误的同义词。 断言根据自己的语义validation您的代码。

断言不是输入validation机制。 如果输入在生产环境中确实是正确的或错误的,即对于输入输出层,则使用其他方法,例如exception或良好的旧条件检查。

Java的断言并不是真正用于参数validation – 它特别声明不要使用断言而不是使用旧的IllegalArgumentException (也不是它们在C-ish语言中的使用方式)。 它们更适用于内部validation,让您对代码进行假设,而这些代码在查看时并不明显。

至于关闭它们,你也可以用C(++)来做,只是如果某人有一个无断言的构建,他们就无法打开它。 在Java中,您只需使用适当的VM参数重新启动应用程序。

我认为断言使用的方式是解释和设想的。

如果您真的想在实际生产代码中添加检查,为什么不直接使用If或任何其他条件语句?

那些已经存在于语言中的东西,断言的想法只是让开发人员添加断言,只有当他们真的不希望这种情况发生时。

例如,检查一个对象为null,假设一个开发人员编写了一个私有方法,并在他知道传递非空对象的类中从两个地方调用它(这不是理想的例子,但可能适用于私有方法),而不是添加不必要的检查,如果从今天起你知道没有办法对象将为null但是如果有人明天用null参数调用此方法,在开发人员的unit testing中,由于存在断言而在最终代码中你仍然可以捕获不需要检查。

我所见过的每一种断言的语言都有能力将它们关闭。 当你写一个断言时,你应该想到“这是愚蠢的,宇宙中没有办法可能是假的” – 如果你认为它可能是假的,那应该是一个错误检查。 如果出现可怕的错误,那么断言只是为了在开发过程中帮助你; 当您为生产构建代码时,禁用它们以节省时间并避免(希望)多余的检查

断言旨在确保您确信您的代码实现的内容真正得到满足。 在产品的开发阶段,它有助于调试,并且在代码发布时通常会被省略。

我错过了什么?

你没有按照它们的使用方式使用断言。 你说“检查输入参数的有效性” – 这正是你不想用断言validation的那种东西。

这个想法是,如果断言失败,你100%的代码中都有一个错误。 断言通常用于比其他方面表现出来更早地识别错误。

对于代码维护者来说,断言实际上是一个非常简洁的文档工具。

例如我可以写:

foo应为非null且大于0

或者把它放到程序的主体中:

 assert foo != null; assert foo.value > 0; 

它们对于记录私有/包私有方法以表达原始程序员不变量非常有价值。

为了获得额外的好处,当子系统开始出现片状时,您可以打开断言并立即添加额外的validation。

这听起来很对。 断言只是一种对调试代码很有用的工具 – 它们不应该一直打开,特别是在生产代码中。

例如,在C或C ++中,在发布版本中禁用断言。

如果断言无法关闭,那么为什么它们甚至存在。

如果要对输入执行有效性检查,则可以轻松编写

 if (foobar<=0) throw new BadFoobarException(); 

或者弹出一个消息框或任何在上下文中有用的内容。

断言的全部意义在于它们可以打开以进行调试并关闭以进行生产。

这并没有直接回答你关于assert的问题,但我建议你查看guava / google-collections中的Preconditions类。 它允许你编写这样的好东西(使用静态导入):

 // throw NPE if listOfStuff is null this.listOfStuff = checkNotNull(listOfStuff); // same, but the NPE will have "listOfStuff" as its message this.listOfStuff = checkNotNull(listOfStuff, "listOfStuff"); 

看起来像这样的东西可能是你想要的东西(并且它不能被关闭)。

断言不是最终用户可以看到的。 它们是程序员,因此您可以确保代码在开发过程中正在做正确的事情。 测试完成后,由于性能原因,断言通常会被关闭。

如果你预计在生产中会发生一些不好的事情,例如listOfStuff为null,那么你的代码就没有经过足够的测试,或者在你让代码拥有它之前你没有清理你的输入。 无论哪种方式,“if(坏东西){抛出exception}”会更好。 断言是用于测试/开发时间,而不是用于生产。

如果您愿意在断言失败时向最终用户支付1美元,请使用断言。

断言失败应该表明程序中存在设计错误。

一个断言表明我已经设计了这个程序,我知道并保证指定的谓词总是成立。

断言对我的代码的读者很有用,因为他们看到(1)我愿意在该属性上设置一些钱; (2)在以前的处决和测试案件中,财产确实存在。

我的赌注假定我的代码的客户遵守规则,并遵守他和我同意的合同。 这个合同可以容忍(允许所有输入值并检查有效性)或要求(客户和我同意他永远不会提供某些输入值[描述为先决条件],并且他不希望我检查这些值一遍又一遍地)。 如果客户遵守规则,但我的断言仍然失败,客户有权获得一些赔偿。

断言是指示代码中可以恢复的问题,或者作为调试辅助。 您应该使用更具破坏性的机制来处理更严重的错误,例如停止程序。

它们还可用于在应用程序稍后在调试和测试方案中失败之前捕获不可恢复的错误,以帮助您缩小问题范围。 部分原因是完整性检查不会降低生产中经过良好测试的代码的性能。

此外,在某些情况下,例如资源泄漏,情况可能并不理想,但停止计划的后果比继续下去的后果更糟。