Kotlin 删除受检异常背后的想法是什么?

4 exception intellij-idea checked-exceptions kotlin

当我们在 Java 中调用抛出异常的方法时,我们如何处理这个问题?

我的代码在 kotlin 中,我使用的是用 Java 编写的第 3 方库。我调用了这个库的一个方法,在某些情况下它会抛出一些自定义异常。现在 kotlin 不强制我处理这个异常,在这种情况下应用程序会崩溃。处理这个问题的最佳方法是什么?

gid*_*dds 5

当 Java 引入检查异常时——编译器强制大多数异常要么被捕获要么被声明为抛出 - 这是一个相当新的想法。Java 是一种比它的大多数前辈更安全的语言:所有行为都定义并在所有平台上保持一致,以及许多旨在防止崩溃、意外行为或脆弱编码的功能。因此,检查异常非常符合该语言的哲学。

但在此后的几年里,许多人得出结论,检查异常是一个错误。我并不完全相信,但这里有一些原因:

  • 他们很冗长。  方法通常包含许多trycatch块,和/或声明许多异常。此外,如果在抛出异常和捕获异常之间有 6 级方法调用,则需要通过 5 个方法声明异常。

    Kotlin 摒弃了 Java 的大部分样板文件,并且摒弃了检查异常符合这一点。

  • 他们鼓励不良做法

    • 试图在错误的地方处理异常,即在错误的抽象级别,在那里没有任何用处。
    • 无意义的catch块(尤其是危险的块,除了记录错误之外什么都不做)。
    • 捕获/声明Exception而不是特定的子类型。
    • 通过将已检查的异常包装在未检查的异常中来作弊,例如RuntimeException.
  • 它们增加了模块之间的耦合。  如果您正在使用添加新异常的库方法,则需要更新调用该方法的所有代码以处理或重新抛出它。

  • 它们需要在不同抽象级别之间转换异常。(例如,数据库访问层可能必须将套接字超时异常转换为数据库不可用异常。)这很乏味,但对于避免暴露实现细节是必要的。 

  • 它们不适用于继承。  如果您正在实现/覆盖的方法未声明为抛出特定的检查异常,那么您的实现也不能抛出它。

  • 它们不适用于 lambda。  在 Java 中,lambda 是使用 Single-Abstract-Method 接口实现的,因此这些方法必须声明可能的异常(在这种情况下,每个用法都必须处理或声明它),或者 lambda 主体必须捕获异常。这两个选项都极大地增加了概念权重以及使用 lambda 所需的代码,破坏了简洁性,这是它们的主要好处之一。

    Kotlin 的实现非常相似(尽管它使用自己的KFunction接口),并且会遇到同样的问题。

面对所有这些问题,很明显,受检异常充其量是一种喜忧参半。我可以理解为什么 Kotlin 取消了它们。

我确实担心它可能会导致程序不那么健壮(并且文档记录较少)。但是在我使用 Kotlin 的两年里,我没有看到任何明显的例子。所以我暂时不做判断:-)

(另见这个问题。)


至于如何处理您的特定情况,我建议您像 Kotlin 已检查异常时所做的一样:查看您调用的方法可以抛出哪些异常,找出处理它们的最佳方式/位置,并处理他们!仅仅因为 Kotlin 没有强迫您这样做,并不意味着它仍然不是一个好主意!

  • 好的,所以在 kotlin 中我们被迫检查 api 方法,看看它是否抛出任何异常并进行相应的处理。是不是有点乏味?在 Java 中,如果我们不处理,就会出现编译时错误。如果我们错过了怎么办,生产就会崩溃。 (2认同)
  • 是的,这就是它的大小。我和你一样担心...... (2认同)
  • 值得补充的是,列出的许多(尽管不是全部)问题都是由于必须显式声明抛出的异常而引起的,如果 Kotlin 保留检查异常但自动 _inferred_ 抛出异常(就像它对返回类型和其他事物所做的那样),则可以避免这些问题。(请参阅[此处](https://discuss.kotlinlang.org/t/in-future-could-kotlin-have-checked-exceptions/1579/65)开始的讨论。) (2认同)