【问题标题】:Making a checked exception a RuntimeException使检查异常成为 RuntimeException
【发布时间】:2014-10-23 14:29:15
【问题描述】:

我正在开发具有自定义异常的旧系统,该异常在任何地方都使用 gosh-frickity-everywhere。它的灵感来自 ServletException 类,它说“只要你的 Servlet 中有异常,你就会想抛出这个 ServletException”。

随着系统的发展(超过 10 年),出现了一个在更高级别捕获异常的更强大的系统,并且不再需要在此自定义异常中包装每个异常。 (有人可能会说它从来没有,但那是另一回事了。它是一个稳定的应用程序,所以我倾向于不抱怨太多!!)但是我们不会一次重构它们,只是随着时间的推移慢慢地重构它们。

但是,如果自定义异常是运行时异常而不是已检查异常,则可以使事情变得更简单的一件事。这样我们就不需要在任何地方显式地捕获它,并且尚未重构的遗留代码将继续抛出它,就像它们在发生空指针异常时抛出空指针异常一样。

我的问题是... 采用一次检查的异常并使其成为运行时异常有什么副作用?

除了对不必要的检查和抛出声明的警告之外,我想不出任何东西,但如果能从以前一直走这条路的人那里得到意见,那就太好了。

【问题讨论】:

  • 出色地使用“gosh-frickity-everywhere”
  • 这是来自更文明时代的优雅武器。
  • 和你一样,我真的想不出任何短期内的副作用。从长远来看,您可能需要考虑如果此异常一直沿堆栈链向上传播,需要进行哪些处理。
  • 事实上,我们可以处理任何异常,而不仅仅是我们的自定义异常,因为它会向上传播堆栈,这就是我想逐步淘汰自定义异常的原因。但由于我们会随着时间的推移逐步淘汰它,因此我们可以通过将旧代码设为运行时异常来简化使用旧代码的新代码。

标签: java runtimeexception


【解决方案1】:

将已检查的异常更改为未检查的异常对现有的工作代码几乎没有实际影响,但您确实需要注意您的代码中某处catch (RuntimeException ...) 的可能性。此类捕获现在不会拦截您的自定义异常,但如果您取消选中它,它们会这样做。

如果您对引发异常的方法(显然是其中的大多数)进行任何反思,您也可能会遇到问题。

【讨论】:

  • 谢天谢地,我们很少反思。有一点豆子人口,但就是这样。至于捕获 RuntimeException,这是一个很好的观点——我进行了代码库搜索,发现了 7 个实例,它们都很清楚。它们要么也捕获自定义异常,要么具有相同的行为。这是一个很好的观点,它可能会导致不同的执行路径。
【解决方案2】:

类似的事情发生在我必须维护的应用程序的旧模块上。将异常转换为运行时的唯一问题是您可能会丢失粒度,但这完全取决于您来处理。

例如,我们在更深层有大量这样的代码:

catch(IOException e){    
    Throwables.propagate(e);
}

我们在该层上不小心使用了该模式,当我们需要检查异常原因时,我们总是必须获取异常原因,这在更高层中产生了很多样板。直到今天,我认为最好创建一个良好的非检查异常类层次结构以保持粒度。例如

catch(IOException e){
    throw new FileNotCreatedException(e);
}

这样,您可以轻松地在其他层捕获异常并轻松划分错误和回退:

catch(FileNotCreatedException e){
   notifyError(e);
} catch(NoMoreStorageException e){
   releaseSomeStorage();
   retryOperation();
}

【讨论】:

  • 由于该人保持相同的班级名称,我不确定这个答案如何适用。
  • 实际上,随着我们逐步淘汰包含所有内容的自定义异常,并且越来越多地使用实际描述问题所在的异常,我们获得了粒度。
【解决方案3】:

这里有几个我能想到的

  • 运行时异常可能会到达您不希望它到达的层。
    • 例如:一个 Servlet ---> 服务 ---> DAO。 DAO 引发的与数据库交互相关的运行时异常可能会出现在 Servlet 上。清晰的层隔离,每个层在其入口点处理所有异常(检查/运行时)可以确保不会发生这种情况。
  • 运行时异常会使系统处于不一致的状态。
    • 例如:如果序列图看起来像 A 类 --> B 类 ---> C 类,如果 B1 类在 B 和 C 之间注入,则 A 类 ---> B 类 ---> B1 类 - --> C 类那么 A、B、B1 类都不会知道当 C 类中发生此运行时异常时它们可能必须进行清理。事实上,这可能会影响任何依赖注入的语义。

作为我认为的一般经验法则:

  • 当您“预期”异常是正常控制流的一部分并且知道如何处理它时,请使用已检查异常。例如:验证业务规则说帐户余额必须比借方金额大至少 100。
  • 当您“不期望”异常作为正常控制流的一部分时使用未经检查的异常,因此无法处理它,但您知道“层”中的“某些类”会处理它以确保正常降级例如:丢失的数据库连接将由您的“服务”层入口类处理。
  • 永远不要使用错误。只有 JRE 有理由抛出错误。

【讨论】:

  • 具有讽刺意味的是,您为第一个潜在问题描述的场景正是我们想要替换它的原因。现在,所有的异常基本上都被包装起来并被抛出到一个检查自定义异常的过滤器中。如果可能,我想将异常保留在较低层。但是我知道如果还没有发生,您所描述的会如何发生! :)
猜你喜欢
  • 1970-01-01
  • 2023-03-02
  • 1970-01-01
  • 1970-01-01
  • 2021-10-08
  • 2015-03-21
  • 1970-01-01
  • 2023-03-23
  • 2013-05-06
相关资源
最近更新 更多