2010-05-03 78 views
25

我在解决问题的同时,最近在Java中发现了assert关键字。起初,我很兴奋。一些有用的东西,我不知道!一种更有效的方式来检查输入参数的有效性!耶学习!Java断言是否被破坏?

但后来我拿了仔细一看,和“锻炼”为“扼杀出完全”由一个简单的事实,我的热情就没有那么多:你可以把断言关闭*

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

这个假设我是一个调试代码的人。有些不熟悉断言的人可能会看到并假设(相当合理),如果断言消息没有出现在日志中,listOfStuff就不成问题。如果你的第一次遇到assert是疯狂的,你会发现它可能完全被关闭吗?毕竟,这不像是有一个命令行选项可以让你禁用try/catch块。

所有这一切使我对我的问题(这是一个问题,而不是夸夸其谈的借口我保证!):

我缺少什么?

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

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

*好的,从技术上讲,它们实际上是默认关闭的;你必须竭尽全力打开它们。但是,仍然可以完全敲除它们。


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

assert首先是一个调试工具,这个概念对我来说意义重大。

我仍然认为应该在生产环境中禁用非平凡私有方法的输入检查,因为开发人员认为不好的输入是不可能的。根据我的经验,成熟的生产代码是一个疯狂的,庞大的事物,多年来由具有不同程度技能的人开发,针对不同程度的理智的快速变化的要求。即使不好的投入是不可能的,从现在开始六个月的一段草率维护编码可以改变这种状况。 The link gustafc provided(!感谢)包括本为例:

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

禁止生产这种简单的检查,在我看来是愚蠢乐观。但是,这是编码哲学的一个区别,而不是一个破碎的特征。

另外,我可以肯定看到的是这样的值:

assert reallyExpensiveSanityCheck(someObject) : someObject;

我要感谢大家谁花时间帮我明白这个功能;这是非常赞赏。

+10

这看起来很像一个咆哮,而不是一个问题。 – 2010-05-03 15:39:29

+3

你曾经在C或C++中使用过ASSERT吗?你有没有希望你可以打开或关闭他们的手指? – 2010-05-03 15:46:51

+1

@Joachim:我明白了,但是很认真,我在这里寻找启示。这个功能对我来说确实确实有些破裂 - 但这种感觉完全等同于说“我的代码没有看到什么问题,因此编译器必须被破坏!”,只是在更高的层面上。我真的觉得我错过了一些东西。如果你能为我提供一种方式来表达,为什么这个功能让我觉得不如破碎,这不会引起你的咆哮,我正在倾听。 – BlairHippo 2010-05-03 15:47:14

回答

21

assert是一个有用的作品合同设计。在这种情况下,断言可用于:

  • 先决条件检查。
  • 后续检查。
  • 中间结果检查。
  • 类不变检查。

断言可能是昂贵的评估(例如,类不变量,它必须在调用任何公共方法之前和之后举行)。断言通常只在调试版本中用于测试目的;你断言一些不可能发生的事情 - 这些事情是有bug的同义词。断言根据自己的语义验证您的代码。

断言不是输入验证机制。当输入在生产环境中(即输入输出图层)可能确实是正确或错误时,请使用其他方法,例如异常或旧的条件检查。

0

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

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

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

+0

我不同意应该将断言用于预计在生产中发生的可恢复错误。这通常是检查异常的用途。但是,在某些情况下,既不例外,也不适用断言。 – 2010-05-03 16:00:17

4

我认为它的使用方式被解释和设想。

如果您确实想在您的实际生产代码中添加支票,为什么不直接使用If或任何其他条件声明?

那些已经存在的语言,断言的想法只是让开发人员添加断言,只是他们并不真正期望这种情况发生。

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

+0

这使我更加相信单元测试的完整性(或存在!),而不是我认为的明智之举,但是,我看到了它的逻辑。 :-) 谢谢。 – BlairHippo 2010-05-03 17:14:00

4

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

+1

我认为这是哲学上的差异,绊倒了我。对我而言,检查输入参数的合理性并不是评论他们是否可能是错的;这是说这种方法需要他们不会错。当然,我可能认为不好的投入是不可能的,但是我知道什么?代码基础非常庞大,我对它的理解并不完整,并且在将来的某个时候,一些白痴(比如我知道)可能会再次导致错误的输入。但话虽如此,我看到能够打开或关闭昂贵支票的价值。 – BlairHippo 2010-05-03 17:18:46

+0

“它应该是一个错误检查”:你的意思是,在程序输入数据的错误,而不是检查程序本身的错误? – Raedwald 2011-09-14 11:03:31

19

Java的断言并非真正用于参数验证 - 它是specifically stated that assertions are not to be used instead of dear old IllegalArgumentException(也不是它们如何用于C-ish语言)。他们更多的是用于内部验证,让您对代码进行假设,从代码中查看它并不明显。

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

+1

这并不完全是链接所说的。它表示它们不能用于PUBLIC方法中的参数验证 - 它明确给出了一个在PRIVATE方法中使用它们进行参数验证的示例。并且对于不明显的代码做出假设,是不是典型的评论工作?虽然不知道C/C++,我绝对看到如何通过命令行切换打开它比完全重新编译更具吸引力。 – BlairHippo 2010-05-03 16:34:16

+0

是的,你可以对某些私有方法使用断言。但是在生产中不应该对这些私有方法进行无效调用(如果不能保证这一点,请使用异常或其他机制)。该页面明确指出,断言应该用于记录假设,而不是评论。请参阅'i%3 == 2'示例。 – 2010-05-03 16:41:16

1

这并不直接回答你关于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"); 

好像这样的事情可能是你想要什么(它不能被关闭)。

1

断言不是供最终用户看到的。它们是给程序员的,所以你可以确保代码在正在开发的过程中做的是正确的事情。一旦测试完成,断言通常会因性能原因而关闭。

如果您预计生产中会发生什么不良情况,例如listOfStuff为空,那么您的代码没有经过足够的测试,或者在让代码拥有它之前您没有对输入进行消毒。无论哪种方式,“如果(坏东西){抛出异常}”会更好。断言用于测试/开发时间,不用于生产。

4

断言是为了确保您确信您的代码完全符合要求。这是在产品开发阶段的调试帮助,通常在代码发布时会被省略。

我错过了什么?

你没有按照他们打算使用的方式使用断言。你说“检查输入参数的有效性” - 这正是你要做的事情而不是想验证断言。

这个想法是,如果断言失败,你100%在你的代码中有一个错误。断言通常用于早期识别错误,否则就会出现。

+1

“你100%在你的代码中有一个错误”:在代码本身或断言检查的代码中。 – Raedwald 2011-09-14 11:07:33

3

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

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

+1

...以及默认的Java。 – DJClayworth 2010-05-03 16:33:23

3

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

例如我可以写:

FOO应非空和大于0

更大 或把这种成程序的主体:

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

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

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

2

如果断言无法关闭,那么他们为什么应该存在。

如果你想的Performa上输入合法性检查,你可以随便写

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

或者弹出一个消息框,或无论是在上下文中。

断言的整个观点是,它们可以打开进行调试并关闭生产。

1

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

断言失败应该表示程序中的设计错误。

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

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

我的赌注假设我的代码的客户坚守规则,并遵守他和我约定的合同。该合同可以容忍(所有输入值允许和检查有效性)或要求(客户端和我同意他将永远不会提供某些输入值[描述为预置条件],并且他不希望我检查这些值一遍又一遍地)。 如果客户坚守规则,而我的主张仍然失败,客户有权获得一些补偿。