有哪些真实的例子来理解断言的关键作用?
当前回答
什么时候应该使用Assert ?
很多很好的答案解释了assert关键字的作用,但很少回答真正的问题,“在现实生活中什么时候应该使用assert关键字?”答案是:
几乎从来没有
断言,作为一个概念,是很棒的。好的代码有很多if(…)throw…语句(以及它们的亲戚,如对象。requireNonNull和Math.addExact)。然而,某些设计决策极大地限制了assert关键字本身的效用。
assert关键字背后的驱动思想是不成熟的优化,其主要特性是能够轻松地关闭所有检查。事实上,断言检查在默认情况下是关闭的。
然而,在生产中继续执行不变检查是非常重要的。这是因为完美的测试覆盖率是不可能的,所有的产品代码都会有错误,而断言应该有助于诊断和减轻这些错误。
因此,if(…)的使用抛出…应该是首选的,就像检查公共方法的参数值和抛出IllegalArgumentException时需要它一样。
偶尔,人们可能会被诱惑编写一个不变检查,它确实需要很长时间来处理(并且经常被调用,以至于它很重要)。然而,这样的检查将减缓测试,这也是不可取的。这种耗时的检查通常被写成单元测试。然而,出于这个原因,有时使用assert是有意义的。
不要使用assert,因为它比if(…)throw…(我非常痛苦地说,因为我喜欢干净和漂亮)。如果您无法控制自己,并且可以控制应用程序的启动方式,那么可以随意使用断言,但始终在生产环境中启用断言。不可否认,这是我倾向于做的事情。我正在推动一个lombok注释,它将导致assert的行为更像if(…)throw ....请在这里投票。
(咆哮:JVM开发者是一群糟糕的、过早优化的编码员。这就是为什么你会在Java插件和JVM中听到这么多安全问题。他们拒绝在产品代码中包含基本的检查和断言,而我们正在继续为此付出代价。)
其他回答
断言用于检查后置条件和“永不失败”的前提条件。正确的代码应该永远不会使断言失败;当它们触发时,它们应该指出一个错误(希望是在接近问题的实际位置的地方)。
断言的一个例子可能是检查一组特定的方法是否以正确的顺序被调用(例如,在迭代器中hasNext()在next()之前被调用)。
Assert在开发时非常有用。当代码正常工作时,某些事情就不能发生。它易于使用,并且可以永远保留在代码中,因为在现实生活中它将被关闭。
如果这种情况在现实生活中有任何可能发生,那么您必须处理它。
我喜欢它,但不知道如何在Eclipse/Android/ADT中打开它。即使在调试时,它也似乎是关闭的。(有一个关于这个的线程,但它指的是“Java虚拟机”,它不会出现在ADT运行配置中)。
下面是我在Hibernate/SQL项目的服务器中编写的断言。一个实体bean有两个有效的布尔属性,称为isActive和isDefault。每个都可以有一个值“Y”或“N”或null,被视为“N”。我们希望确保浏览器客户端仅限于这三个值。所以,在这两个属性的setter中,我添加了这样的断言:
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
注意以下几点。
This assertion is for the development phase only. If the client sends a bad value, we will catch that early and fix it, long before we reach production. Assertions are for defects that you can catch early. This assertion is slow and inefficient. That's okay. Assertions are free to be slow. We don't care because they're development-only tools. This won't slow down the production code because assertions will be disabled. (There's some disagreement on this point, which I'll get to later.) This leads to my next point. This assertion has no side effects. I could have tested my value against an unmodifiable static final Set, but that set would have stayed around in production, where it would never get used. This assertion exists to verify the proper operation of the client. So by the time we reach production, we will be sure that the client is operating properly, so we can safely turn the assertion off. Some people ask this: If the assertion isn't needed in production, why not just take them out when you're done? Because you'll still need them when you start working on the next version.
Some people have argued that you should never use assertions, because you can never be sure that all the bugs are gone, so you need to keep them around even in production. And so there's no point in using the assert statement, since the only advantage to asserts is that you can turn them off. Hence, according to this thinking, you should (almost) never use asserts. I disagree. It's certainly true that if a test belongs in production, you should not use an assert. But this test does not belong in production. This one is for catching a bug that's not likely to ever reach production, so it may safely be turned off when you're done.
顺便说一句,我可以这样写:
assert value == null || value.equals("Y") || value.equals("N") : value;
对于只有三个值是可以的,但是如果可能值的数量变大,HashSet版本就会变得更方便。我选择HashSet版本来说明我关于效率的观点。
我不知道Java中这个特性背后的历史。但是我有一个关于断言可以用来做什么的想法,我认为在这个帖子中没有提到过。
假设你有一个枚举,你打开它:(†)
public enum Veggie {
CAULIFLOWER,
CARROT,
}
// Another class
switch (veggie) {
case CAULIFLOWER: value = 5;
case CARROT: value = 3;
}
†:也许优秀的Java编译器/工具会静态地捕获丢失的枚举值。在这种情况下,想象一下你发现了一些bug并修复了它。然后,您可以使用下面描述的技术在代码中适当地添加回归测试。
这里没有好的默认情况。所以你要确保你覆盖了所有的值。当你添加一个新的enum变量时,你要确保你更新了switch块:
public enum Veggie {
CAULIFLOWER,
CARROT,
TOMATO,
}
但事实证明,当用户在web应用程序中加载特定视图时,这段代码会被调用。尽管存在这个错误,视图仍然可以持续存在:这将是一个bug,但不值得干扰视图的加载(让我们说用户只是看到了一些错误的数字)。所以你只需要记录一个警告:
switch (veggie) {
case CAULIFLOWER: value = 5;
case CARROT: value = 3;
default: nonExhaustiveMatchOnEnum();
// […]
public static void nonExhaustiveMatchOnEnum() {
String errorMessage: "Missing veggie";
logger.warn(errorMessage);
}
但是当你在开发代码时,你确实希望这个错误立即失败——开发者可以在五分钟内修复这个错误,而不像你的web应用程序的用户。
所以你可以添加一个assert false:
public static void nonExhaustiveMatchOnEnum() {
String errorMessage: "Missing veggie";
assert false : errorMessage;
logger.warn(errorMessage);
}
现在,应用程序将在本地崩溃,但只在生产中记录一个警告(假设您在本地使用java -ea,而不是在生产中)。
断言主要用于调试应用程序,或者用于替换某些应用程序的异常处理,以检查应用程序的有效性。
断言在运行时工作。这里有一个简单的例子,可以非常简单地解释整个概念——assert关键字在Java中做什么?(WikiAnswers)。
推荐文章
- Intellij IDEA Java类在保存时不能自动编译
- 何时使用Mockito.verify()?
- 在maven中安装mvn到底做什么
- 不可变与不可修改的集合
- 如何在JSON中使用杰克逊更改字段名
- GSON -日期格式
- 如何从线程捕获异常
- 无法解析主机"<URL here>"没有与主机名关联的地址
- 如何在Java中打印二叉树图?
- String.format()在Java中格式化双重格式
- com.jcraft.jsch.JSchException: UnknownHostKey
- Java中的操作符重载
- 如何加速gwt编译器?
- 在Hibernate中重新连接分离对象的正确方法是什么?
- 应该……接住环内还是环外?