在 Java 中主动抛出 AssertionError 是好的做法吗?

2022-09-01 14:24:56

在阅读 Joshua Bloch 的“Effective Java - Second Edition”时,我偶然发现了第 152 页上的以下代码:

double apply(double x, double y) {
    switch(this) {
        case PLUS:   return x + y;
        case MINUS:  return x - y;
        case TIMES:  return x * y;
        case DIVIDE: return x / y;
    }
    throw new AssertionError("Unknown op: " + this);
}

现在让我感到困惑的是,这是主动抛出的。这被认为是好的做法吗?根据我的理解,断言用于不与代码交错,这样当java编程在没有启用断言的情况下启动并且因此不执行断言语句时,行为不会改变。如果我在运行程序时甚至不启用断言时会得到一个,我会相当困惑。AssertionErrorAssertionException

尽管我知道示例情况可能会经常发生,但您分析了几个不同的选项,如果它们都不是,则应抛出异常。

那么,在这里扔一个是好的做法,还是扔一个不同的更好?如果是这样,哪一个最适合?或?AssertionExceptionIllegalArgumentException


编辑澄清:我的问题不是关于我们是否应该在这里抛出一个,而是如果我们想抛出一个或一个,它应该是哪一个?主动投掷s是好的做法吗?文档说抛出以表明断言已失败,因此我感觉我们不应该主动抛出它。这是对的吗?ErrorExceptionErrorAssertionError


第二次编辑:明确的问题:积极抛出一个,还是应该避免,即使这是可能的,也是好的做法?(我猜阅读文档是后者)AssertionError


答案 1

我同意布洛赫先生的观点 - 替代方案(,和)不能正确传达问题的严重性,呼叫者可能会错误地尝试捕获和处理这种情况。事实上,如果达到这条线,有问题的程序就会被破坏,唯一理智的事情就是退出。IllegalArgumentExceptionIllegalStateExceptionUnsupportedOperationException

这里的要点是枚举具有一组有限的值,因此应该不可能到达该行 - 仅当枚举的定义已更改而没有修复此实例方法时才会发生这种情况。抛出一个表示调用方犯了一个错误,而实际上方法(和枚举)本身已经损坏。显式正确引发 an 表示此方法预期的不变量已被违反。throwRuntimeExceptionAssertionError

番石榴有一篇有用的文章,它分解了何时提出不同类型的例外。他们写道:

常规断言是一种检查,仅当类本身(包含检查)以某种方式中断时,该检查才会失败。(在某些情况下,这可以扩展到包。它们可以采取各种形式,包括后置条件、类不变量和内部前提条件(在非公共方法上)。

不可能的条件检查是不可能失败的检查,除非以后修改周围的代码,或者我们关于平台行为的最深层次的假设被严重违反。这些应该是不必要的,但通常是强制的,因为编译器无法识别语句无法访问,或者因为我们对编译器无法推断的控制流有所了解。

该页面显示a是处理这些情况的推荐方法。验证类中的注释还提供了有关选择例外的一些有用见解。在看起来太强的情况下,提高一个可能是一个很好的折衷方案。AssertionErrorAssertionErrorVerifyException

至于 或 的具体问题,这并不重要(两者都未经检查,因此可能会在调用堆栈中向上移动而不会被捕获),但调用方更有可能尝试从 中恢复。在这种情况下使应用程序崩溃是一种功能,因为否则我们将继续运行(此时)明显不正确的应用程序。当然,呼叫者不太可能接听和处理(或或),但当然呼叫者可以做任何他们想做的事情。ErrorRuntimeExceptionRuntimeExceptionAssertionErrorErrorThrowable


答案 2

在我看来,在这里使用一个是不正确的。AssertionError

从文档中AssertionError 扩展了基类 Error

Error 是 Throwable 的一个子类,它指示合理的应用程序不应尝试捕获的严重问题。

错误应该是致命的,而我希望您的程序能够处理此错误,并向用户显示有关未知操作的警告消息。

如果这里有任何东西,我希望抛出一个不受支持的OperationException,并在调用堆栈中的其他位置进行处理。

抛出以指示不支持请求的操作。

考虑以下情况:不是在计算器中,而是在使用 ENUM 的任何代码流中:

如果开发人员要向现有枚举添加新值,我不会期望利用此现有枚举的函数调用错误,仅仅因为不支持新值。


推荐