我应该为无效输入抛出已检查或未检查的异常吗?

Should I throw checked or unchecked exception for invalid input?

我正在编写一个需要一些输入的方法。现在,我认为以防御方式编写它是一个很好的做法(即检查输入是否正确)。

基本上,输入应该没问题,因为只有一个地方(可靠)使用这种方法。

所以我的问题是,

  1. 此处的防御性编程合理吗?
  2. 如果是这样,它应该抛出已检查异常还是未检查异常?

想想如果您没有进行该检查并且输入了一些无效输入,系统或应用程序的其余部分会发生什么。

你有一个安全的默认值可以备用和使用吗? 所有 个案例的正确值吗?您的 code/API 的 caller/client 是否需要通知这件事? 因此,基于此,您甚至可以只记录警告并继续使用默认值。但在大多数情况下,情况并非如此。

[..] there's only one place (reliable) that uses this method.

截止到今天。将来,它可能会改变。所以,IMO,你可以检查输入是否有效。

should it throw a checked or unchecked exception?

这又把我们带回了您 code/API 的来电者或客户。你想让他们期待它发生并处理它吗? 如果他们可以处理异常并恢复,那么它就有意义(注意:这意味着所有调用者在调用您的 API/code 时都需要有一个 try..catch 块)。否则,如果它们 应该 传递一个有效值,如果它们不传递,则会出现未经检查的异常(在 contract/Javadoc 中明确说明)

让我们看一下 Java 库本身的例子。

Fileclass中的许多方法抛出检查异常。

如果输入无效,

Integer.valueOf 会抛出一个 NumberFormatException(这是一个未经检查的异常)。


一个好的做法是,任何 public API 必须 验证其输入。

另外,看看Java: checked vs unchecked exception explanation 在决定之前

我没有考虑到你的整个问题,所以我会改变我的答案。

Should you check input?

使用 public 方法 - 总是。原因是最佳实践总是调用接口而不是实现。因此,当您拥有一个界面时,您希望任何使用您的实现的人在传递无效参数时都能获得适当的反馈。

Should you throw exceptions?

是的!正如 Bob 大叔所说,忘记捕获异常比忘记检查 return 值来检查错误要好。换句话说,异常更简单,因为它们不会失败 "silently".

在调用的每一层中以惯用的方式乘以相同的输入检查通常是一种不好的做法:你重复自己,因为它的变化很快就会变成地狱或一团糟。
所以我认为,如果您的方法被设计为 API 可由多个客户端 类 使用,它应该进行此检查,即使它可能是多余的。否则就保持干燥。

请注意,确保 API 一致性的最佳方法是通过单元测试覆盖您的方法。
通过这种方式,您仅在其功能要求需要时才提供检查,并且您不会想太多。

If so, should it throw a checked or unchecked exception?

使用已检查或未检查异常的用法非常值得商榷。
理论上,当需要客户端处理异常时,检查异常非常有用。但在实践中,如果客户端不想处理它或者如果该方法在 lambda 体内使用,它可能会变成噪音。

Basically, the input should be okay since there's only one place (reliable) that uses this method.

太好了,您以这种方式推理您的函数。您刚刚为您的功能定义了一个合同。它表示,此函数 需要 有效输入。在这种情况下,通过在检测到无效输入时抛出异常来采用防御性编程是一种很好的做法。此异常将大大简化对您的函数调用者的检测,这些调用者破坏了合同并使用无效输入调用了您的函数。

如果您没有抛出专用异常,那么(如果幸运的话)您的函数可能会因抛出通用技术异常而中断,例如NullPointerException 或者 ArrayIndexOutOfBoundsException。这样的堆栈跟踪仍然表明,在调用您的函数时发生了错误。但是,你需要分析,"what"发生了。在最坏的情况下,您的函数根本不会抛出任何异常并且 "somehow" 处理无效输入。您的程序稍后可能会在看似无关的位置中断,或者在更糟糕的情况下,它不会中断并且会向用户显示错误的结果。

Is the defensive programming justified here?

是的,绝对是。

If so, should it throw a checked or unchecked exception?

已检查异常与未检查异常仍在争论中。您可以在 Whosebug 上找到这个问题的许多答案。 Scala 语言只有未经检查的异常。我个人也比较喜欢。

What if your function expects invalid input too?

通常,这些是接收人类输入的函数。例如用户注册表要求密码,密码不是太短,至少包含一个数字。在这种情况下,"failure" 是预期的,并且应该以某种方式编码在函数的 return 值中,以便函数的调用者被迫检查两种 return 值 - 成功和失败。这样的 return 值可以使用 algebraic data types 建模。 Java 不允许自然地对 ADT 进行建模,我们习惯于通过抛出异常来对失败进行建模。也许在这种情况下检查异常仍然有意义。

编辑::其他答案详细介绍了 "when" 验证以及何时不验证。总结一下:public 方法 - 是的;不要跨层重复相同的验证。