【问题标题】:Why should you wrap checked exceptions in lambda's in an unchecked exception?为什么要将 lambda 中的已检查异常包装在未检查异常中?
【发布时间】:2020-01-20 19:21:16
【问题描述】:

我已经成为一名 Java 开发人员大约两年了,如果说我真正喜欢这门语言的一件事,那就是流 API。它使我的代码更加流畅、简洁和易读。

但困扰我的一件事是在使用 lambda 时应该如何处理异常。我已经阅读了有关该主题的几篇文章,并在 StackOverflow 上看到了几个问题。 普遍的共识似乎是您必须包装方法调用以捕获已检查的异常并重新抛出未检查的异常。 (像这样:https://www.oreilly.com/ideas/handling-checked-exceptions-in-java-streams

但我尚未阅读或理解为什么这是一个好主意。检查异常的优点是调用者知道会发生什么。我被告知,无论何时你想强制执行业务规则,检查例外都是要走的路。应为应用程序无法从异常中恢复的情况保留未经检查的异常。那么为什么他们在处理流时几乎强迫我们使用未经检查的异常呢?乍一看,这似乎是一个很大的缺陷,对吧?

我目前正在编写一个 SOAP Web 服务,一个很大的优势是所有可能引发的业务故障都得到了很好的定义。客户端可以相应地响应抛出的异常。 抛出运行时异常会导致客户端收到 SOAPFaultException,不知道到底出了什么问题或如何处理它。

我可以使用全局异常处理程序并捕获所有可能的 RTE(假设我没有忘记一些),然后再次将它们作为 XSD/WSDL 中定义的已检查异常之一重新抛出。 但是我很难说服我的高级同事相信函数式编程的好处,并告诉他们将已检查的异常包装在未检查的异常中,只是将它们转回已检查的异常绝对无助于我的困境。

那么为什么在 lambda 中捕获已检查的异常并将它们包装在未检查的异常中被认为是这样做的好方法呢?很少有其他场景我们会真正捕获已检查的异常并抛出未检查的异常。 那么为什么在使用 lambda 时它被认为是一个不错的选择呢? 真的是因为考虑到语言的限制,这是唯一可能的方法(对于 Lombok 的@SneakyThrows 等一些肮脏的变通方法是安全的)? 那么你如何处理所有这些非制导导弹?

【问题讨论】:

  • 这不是关于“成为一个好主意”。这是关于“成为编译代码的唯一正确方法”。现在普遍的共识是,检查异常一开始是个坏主意。它们确实不适用于功能性惯用语,会引入混乱,并且经常会出现错误(因为新手会抓住它们而不是扔掉它们)。
  • 您应该阅读 Joshua Bloch 的 Effective Java 的最新版本。我们这些年长的人在某个时候都接受了“检查的异常是好的”的口头禅(对我来说,那是大约 1997 年 - 2003 年 4 月,当我开始质疑设计时。在 2005 年 6 月左右,它变得更广泛地承认毕竟这个想法不太好)。
  • @Santorius:我有第 3 版。第 296 页:对可恢复条件使用检查异常,对编程错误使用运行时异常。这基本上就是我一直在做的事情。我知道我公司中没有真正从不使用检查异常的 Java 开发人员。我知道鲍勃叔叔对此事的看法,但我仍然不明白为什么受检异常如此糟糕...如果您一直使用未受检异常,如果您忘记处理它们,您的程序可能会无意中停止工作适当地。我一定会在这个主题上做更多的阅读。

标签: java exception lambda java-stream


【解决方案1】:

为什么要将 lambda 中的已检查异常包装在未检查异常中?

简短的回答:因为你必须这样做。 (差不多。)

那么为什么他们在处理流时几乎强迫我们使用未经检查的异常呢?乍一看,这似乎是一个很大的缺陷,对吧?

无需进入历史和关于检查异常是否是错误的辩论......

流和 lambda 中的异常处理实际上是 Java 异常和异常处理的通用设计的直接结果。后者无法改变:这对甲骨文现有的 Java 客户群来说太具有破坏性了。因此,在可预见的未来,我们会坚持这一点。

真的是因为考虑到语言的限制,这是唯一可行的方法(对于 Lombok 的@SneakyThrows 等一些肮脏的变通方法是安全的)?

是的,这是唯一的方法。不管是不是好办法。

那么你将如何处理所有这些非制导导弹?

一种方法是简单地让未经检查(或偷偷检查)的异常传播。在大多数情况下,应用程序或线程会崩溃,您可以使用日志文件中的堆栈跟踪来查找/修复根本原因。这假设原始检查异常不是“预期的”,因此无法真正恢复。

如果您试图让您的应用程序从检查的异常中恢复:

  • 对于包装的异常,您可以捕获RuntimeException,然后使用ex.getClass()ex.getCause() 来决定要做什么。

  • 对于偷偷检查的异常,您可以捕获Exception 并使用instanceof 来区分(预期的)偷偷检查的异常和未检查的异常。

【讨论】:

  • 感谢您的回答。我不太喜欢偷偷摸摸的检查异常,此时我也不同意您应该始终使用未经检查的异常,即使应用程序可以从错误中恢复。我将尝试您将其包装在 RTE 中并根据原因重新抛出的建议。为此编写一个实用程序类不会那么难。如果它不起作用,我总是可以回到命令式的编写代码的方式来为抛出检查异常的方法编写代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
  • 2010-10-03
  • 1970-01-01
  • 2016-10-16
  • 1970-01-01
相关资源
最近更新 更多