有哪些真实的例子来理解断言的关键作用?


当前回答

什么时候应该使用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()之前被调用)。

一个真实的例子,来自一个stack类(来自Java文章中的断言)

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

默认情况下,断言是禁用的。要启用它们,我们必须使用-ea选项运行程序(粒度可以改变)。例如,java -ea AssertionsDemo。

使用断言有两种格式:

简单:如。维护1 = = 2;//这将引发AssertionError。 更好的方法:断言1==2:“不可能..1不等于2”; 这将引发AssertionError,并显示给定的消息,因此更好。虽然实际的语法是断言expr1:expr2,其中expr2可以是返回值的任何表达式,但我更多地使用它只是为了打印消息。

我不知道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 1.4中添加的。它们用于验证代码中不变量的正确性。它们永远不应该在生产代码中触发,并且指示了代码路径的错误或误用。它们可以在运行时通过java命令上的-ea选项激活,但默认情况下不会打开。

一个例子:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}