什么是AssertionError?在哪些情况下应该在自己的代码中抛出它?

90
在《Effective Java 第二版》的第 2 条中,有这段代码片段,其中作者想禁止对象的空初始化。
class Example {
    private Example() {
        throw new AssertionError();
    }
}

这里让我困惑的是抛出异常的类型。

我不明白 AssertionError 是仅仅因为缺少更合适的错误而被抛出,还是应该是这样的。

据我所知,当一个 assert 语句失败时,框架会抛出这个错误。此外,在 javadoc 中只是简单地写道:

[AssertionError 是] 抛出以表示断言失败。

但我没有看到任何断言(真假语句)在此被违反。当然,“您不应该实例化此类的对象”语句已经被违反了,但如果这是其背后的逻辑,那么我们应该随处抛出 AssertionError,显然这并不是发生的情况。

顺便说一下,我会直接抛出

new IllegalStateException("Must not instantiate an element of this class")

那里有什么问题吗?在哪种情况下我应该在自己的代码中引发AssertionError

如果这只是一个微妙的疑问,我很抱歉,但我在我的代码中经常使用这种模式,我想确保我做得对。


3
“Assertions”不仅仅指“assert”语句。 - user253751
2
Guava有一个有用的参考资料,比较了运行时异常和AssertionError的几种不同用例。我鼓励任何对这种模式感到惊讶的人阅读这个页面。 - dimo414
6个回答

65
当然,“不应该实例化此类的任何项”这个声明被违反了,但如果这是其背后的逻辑,那么我们应该到处抛出AssertionErrors,而显然这不是发生的事情。
代码并没有说用户不应该调用零参数构造函数。断言在这里是用来表明,就程序员所知,他/她已经使得调用零参数构造函数变得不可能(在这种情况下通过将其设置为私有并且不从Example的代码中调用它)。因此,如果调用发生,则违反了该断言,因此使用AssertionError是恰当的。

4
明白了。那么抛出该异常就好比同时创建断言并将值false赋给它,您同意这种说法吗? - doplumi
@domenicop:你搞定了!! - Oliver
请注意,您还可以使用 assert false。有关详细信息,请参阅此处:https://dev59.com/SWEh5IYBdhLWcg3wwly8 - Maarten Bodewes
15
请注意,使用 assert falsethrow new AssertionError(); 是不同的。前者仅在启用断言时抛出错误,而后者始终会抛出断言。 - Mathias Bader

49

AssertionError 的意思是出现了开发人员认为不可能发生的情况。

因此,如果出现 AssertionError,这清楚地表明存在编程错误。


24

当您编写的代码不应该执行但是实际发生了时,根据您的逻辑,会抛出断言错误(Assertion Error)。如果这种情况发生了,您不需要捕获它。在这种情况下,您应该抛出一个断言错误。

new IllegalStateException("Must not instantiate an element of this class")' // Is an Exception not error.

注意:断言错误属于java.lang.Error,而错误不应该被捕获。


谢谢,我实际上在这里混淆了RuntimeException和Error,不过我想知道它们之间的区别是否仅在于“它们不应该被捕获”,还是“如果你捕获它们,编译器将拒绝编译你的代码”。 - doplumi
2
不,你可以像这样做 catch(Throwable t){} 但这是非常糟糕的代码。 错误和Throwable不应该被捕获。比如说你遇到了一个OutOfMemoryError,如果你捕获了它,那么想象一下你已经没有RAM了,但你的应用程序仍在运行。这将不会是好的结果。你会把你的电脑炸掉(开个玩笑)。 - Oliver

11

虽然我来晚了,但是大多数答案好像都是关于一般情况下为什么和何时使用断言,而不是特别涉及到AssertionError的使用。

assertthrow new AssertionError() 非常相似,拥有相同的概念目的,但是它们之间存在差异。

  1. throw new AssertionError() 不管是否已经通过 -ea 开关启用了断言,都会抛出异常。
  2. 编译器知道 throw new AssertionError() 会退出代码块,因此使用它可以避免某些 assert 无法避免的编译错误。

例如:

    {
        boolean b = true;
        final int n;
        if ( b ) {
            n = 5;
        } else {
            throw new AssertionError();
        }
        System.out.println("n = " + n);
    }

    {
        boolean b = true;
        final int n;
        if ( b ) {
            n = 5;
        } else {
            assert false;
        }
        System.out.println("n = " + n);
    }

第一个代码块可以正常编译。第二个代码块无法编译,因为编译器不能保证在代码尝试打印出变量n之前它是否被初始化。


1
assert len(tf.config.list_physical_devices('GPU')) > 0

如果您正在使用Collab,这将无法正常工作。

使用Jupyter Notebook来使用物理设备GPU。


-4

AssertionError 是一种未经检查的异常,由程序员或API开发人员明确引发,以指示断言语句失败。

assert(x>10);

输出:

AssertionError

如果 x 不大于 10,则会出现运行时异常 AssertionError。

6
错了,错误不等同于异常。具体来说,AssertionError不是RuntimeException的子类,因此它不是未经检查的异常。 - i2B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接