是否应该为无效输入抛出检查或未检查的异常?

时间:2018-11-25 12:30:03

标签: java defensive-programming

我正在写一个需要一些输入的方法。现在,我认为以一种防御性的方式(即检查输入是否正确)来写是一种好习惯。

基本上,输入应该可以,因为只有一个位置(可靠)使用此方法。

所以我的问题是,

  1. 这里的防御性程序合理吗?
  2. 如果是这样,它应该引发检查还是未检查的异常?

4 个答案:

答案 0 :(得分:2)

考虑一下,如果您没有检查并输入了一些无效的输入,将会对系统或应用程序的其余部分造成什么影响。

您是否有一个安全的默认值可供回退和使用?它对 all 个案例是否正确?是否需要通知您的代码/ API的调用者/客户端? 因此,基于此,您甚至可以记录一个警告,并可以继续使用默认值。但是在大多数情况下,情况并非如此。

  

[..]只有一个地方(可靠)使用此方法。

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

  

它应该引发已检查还是未检查的异常?

这又将我们带回您的代码/ API的调用方或客户端。您是否希望他们期望发生并处理它? 如果他们可以处理异常并进行恢复,则这很有意义(注意:这意味着所有调用者在调用您的API /代码时都需要一个try..catch块)。否则,如果应该传递有效值,而如果不传递有效值,则将执行未经检查的异常(在contract / Javadoc中明确说明)

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

File类中的许多方法都会引发一个已检查的异常。

如果输入无效,

Integer.valueOf会抛出NumberFormatException(这是未经检查的期望)。


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

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

答案 1 :(得分:2)

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

请注意,确保API一致性的最佳方法是通过单元测试覆盖方法。
这样,仅当功能需求有要求并且您没有考虑过多时,您才提供检查。

  

如果是这样,它应该抛出一个已检查或未检查的异常吗?

使用检查或未检查的异常用法是值得商<的。
从理论上讲,当要求客户端处理异常时,检查异常非常有用。但是在实践中,如果客户端不想处理它或在lambda主体内部使用该方法,则可能会产生噪音。

答案 2 :(得分:1)

我没有考虑到您的整个问题,因此我将更改答案。

  

您应该检查输入吗?

使用公共方法-始终。之所以如此,是因为最佳实践始终是调用接口而不是实现。因此,当您拥有一个接口时,如果他们使用无效的参数,则希望任何人都能获得正确的反馈。

  

您应该抛出异常吗?

是的!正如鲍勃叔叔所说的,忘记捕获异常比忘记检查返回值检查错误要好。换句话说,异常是更傻瓜的证明,因为它们不会“无声地”失败。

答案 3 :(得分:1)

  

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

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

如果您没有抛出专用异常,那么(如果您很幸运)您的函数可能会因抛出通用技术异常而中断,例如NullPointerExceptionArrayIndexOutOfBoundsException。这样的堆栈跟踪仍然表明,调用函数时发生了错误。但是,您需要分析“发生了什么”。在最坏的情况下,您的函数根本不会引发任何异常,并且“以某种方式”处理无效的输入。您的程序随后可能会在看似无关的位置上中断,甚至在更坏的情况下也不会中断,并且会向用户显示错误的结果。

  

这里的防御性程序合理吗?

是的,肯定是。

  

如果是这样,它应该抛出一个已检查或未检查的异常吗?

检查与未检查异常仍然是一个持续的争论。您可以在StackOverflow上找到许多有关此问题的答案。 Scala语言仅具有未经检查的异常。我个人也喜欢它们。

  

如果您的函数也希望输入无效怎么办?

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

编辑::其他答案在“何时”进行验证以及何时不进行验证方面有更多详细信息。总结一下:公共方法-是的;请勿跨层重复相同的验证。