【问题标题】:why Scala does not have concept of checked and unchecked exception?为什么Scala没有检查和未检查异常的概念?
【发布时间】:2021-09-02 06:15:10
【问题描述】:

我有以下两个问题:

  1. 为什么 Scala 没有 Checked 和 Un-checked 异常的概念?
  2. 不支持检查异常有哪些优点或缺点?

【问题讨论】:

标签: scala exception exception-handling


【解决方案1】:

我不知道 Scala 选择不检查异常的确切原因,但可以说这是 common 方法。除了 java 之外,你还知道多少其他语言有检查异常?

我会指出这些年来我在很多 java 程序中看到的一些东西,我敢肯定,如果你做过认真的 java 编程,你也见过:

try {
  // do stuff
} catch (Exception e) {
  throw new RuntimeException(e);
}

try {
  // do stuff
} catch (Exception e) {
  // do nothing
}

当然,您可以说这是惰性编程,而后者确实如此。但它向你展示了一个问题。您有时不能也不想就地处理异常,您希望程序中断,或者异常冒泡到将处理错误的更高级别的组件。

您现在可能正在考虑您只需要在方法签名中添加 throws,但通常您不能这样做。我可以用 java 8 lambdas 给你一个清楚的例子来说明这个问题。

list.stream().map(item -> {
  // throws a checked exception. compilation error
  return normalizeItem(item);
});

在上面的代码中,您需要使用上面显示的两种技术之一来处理异常。您当然可以创建一个抛出异常的新功能接口,但您需要重新创建所有标准接口,以便使用 throws 对其进行注释。如果你问我,这真是一团糟。

我认为这是原因之一。如您所见,Scala 从一开始就是功能性的,检查异常并不能很好地处理这一点。

您可以阅读更详尽的讨论 here

【讨论】:

    【解决方案2】:

    TL;DR 跳转到最后一段 :)

    虽然我完全同意 Tiago 的回答,但可以添加一些内容。 如您所知,Scala 是一种函数式和面向对象的语言。 它的功能方面表明应该消除side-effects,或者至少尽可能减少。

    抛出异常是一种副作用,因为它不是引用透明的(即,它取决于抛出异常的上下文,例如,如果从 try 块内抛出异常,它将被捕获,而如果它被抛出该 try 块之外,它将改变程序的流程)。

    以下是Scala 中的函数式编程(P. Chiusano, R. Bjarnason)一书中的示例

     def failingFn(i: Int): Int = {
           val y: Int = throw new Exception("fail!")
           try {
             val x = 42 + 5
             x + y
           }
           catch { case e: Exception => 43 }
     }
    

    在上面的代码中,y 不是引用透明的,因为如果你在 try 块中用它的值替换它,函数的结果将会不同。

    好的,关于所有的理论,从上面得到的关键结论是抛出异常是一种副作用,这违反了函数式编程范式。
    为了解决这个问题,Scala 的设计者决定改为返回表示发生异常的“值”,而不是抛出异常。出于这个原因,引入了像 Try(及其直接子类型 Success 和 Failure)这样的类。无需抛出异常,您只需修改函数的返回类型,将其包装在 Try 中。这迫使客户端检查成功或失败,而不会产生抛出异常带来的所有副作用。 Try 类型的引入基本上取代了检查的异常,因为客户端通过使用 Try 返回类型隐式地意识到编译时异常的可能性。

    【讨论】:

      【解决方案3】:

      启动Scala 3.1.0-RC1 可以启用异常检查

      import language.experimental.saferExceptions
      

      例如

      scala> import language.experimental.saferExceptions
      
      scala> def foo(): Int = throw new Exception()
      -- Error:
      1 |def foo(): Int = throw new Exception()
        |                 ^^^^^^^^^^^^^^^^^^^^^
        |        The capability to throw exception Exception is missing.
        |        The capability can be provided by one of the following:
        |         - A using clause `(using CanThrow[Exception])`
        |         - A `throws` clause in a result type such as `X throws Exception`
        |         - an enclosing `try` that catches Exception
        |
        |        The following import might fix the problem:
        |
        |          import unsafeExceptions.canThrowAny
        |
      
      scala> def foo(): Int throws Exception = throw new Exception()
      def foo(): Int $throws Exception
      

      使编译器能够跟踪这些异常的机制称为capabilities,由context functions 实现。请注意,错误能力不同于通过错误单子(例如EitherOptionTry 等)进行的错误效果跟踪

      能力可以表示为参数,而传统上的效果表示为结果值的一些附加。

      这意味着不需要为了跟踪错误而修改返回类型,例如考虑两者的区别

      scala> def foo(): Try[Int] = Success(42)       // we have to wrap 42
      def foo(): util.Try[Int]
      
      scala> def foo(): Int throws Exception = 42    // just pure 42
      def foo(): Int $throws Exception
      

      关于 Scala 没有实现 Java 的检查异常机制的可能原因,document 解释了

      Java 的检查异常模型的主要问题是其缺乏灵活性,这是由于缺乏多态性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多