Scala文档中的此代码块是否有意义?

时间:2019-01-17 01:49:28

标签: scala

此示例来自https://www.scala-lang.org/api/current/scala/util/control/ControlThrowable.html

import scala.util.control.ControlThrowable

try {
  // Body might throw arbitrarily
} catch {
  case c: ControlThrowable => throw c // propagate
  case t: Exception        => log(t)  // log and suppress
}

我理解为什么一定不能捕获Throwable,但是在此示例中,我们可以删除case c: ControlThrowable => throw c // propagate而不会发生任何问题,因为下一个案例捕获了Exception,因此ControlThrowable s无论如何都不会被抓住。我在这里想念东西吗?

3 个答案:

答案 0 :(得分:3)

这不是没有用的。

考虑一下:

class FooException extends Exception with ControlThrowable

这将被第一个方块捕获并重新抛出,再也不会到达第二个方块。编写这样的异常可能不是一个好主意,但是,当首次引入ControlThrowable时(当时称为ControlException),它就是not uncommon in the standard library

答案 1 :(得分:2)

我想扩展Brian McCutchon的答案。我认为有几个不同的方面(高度哲学的方面)值得考虑。

有人可能会说,您可以直接从Throwable继承的原因是因为Java类型系统没有禁止这种扩展的方法,并且Throwable的层次结构并非旨在添加不是ErrorException的子类的更多类。这可能是诸如NonLocalReturnExceptionBreakException之类的东西最初是RuntimeException的子类型而不是直接Throwable的子类型的原因。

另一点是,引入这样的标记时,它必须是trait。同样,在Scala类型系统中,没有办法强制将混合在此特征中的类不是Exception的子类。

这两个事实加在一起意味着在历史上实际上存在ControlThrowable的子类ControlException(然后是Exception)。牢记这一点,很显然,捕获的类型集是这两种情况在设计上没有脱节。是的,在introduction of ControlException之后的将近changed to be ControlThrowable之后的一年,但是那时还没有办法强制所有其他(自定义)子类进行相同的切换。

关于本示例中的代码的另一个哲学观点是,即使程序中没有ControlThrowable子类型的Exception子类型,并且第一种情况确实不会影响第二种情况的行为(今天很可能是这种情况),它仍然表明开发人员已经考虑了这个特定的细节。而且显然,代码应该为其他人编写,就像为计算机编写一样。

最后一点,今天您可能应该使用NonFatal,它包含对ControlThrowable的测试。

答案 2 :(得分:0)

该代码最初写为ControlThrowable's source code comment

就像您提到的那样,您可以删除该行,除非您有可能生成扩展ControlThrowable的自定义Exception,此代码的目的是告诉您不要捕获并抑制ControlThrowable。 如果代码是

try {
  // Body might throw arbitrarily
} catch {
  case c: ControlThrowable => throw c // propagate
  case t: Throwable        => log(t)  // log and suppress
}

然后,可能更容易理解代码试图告诉的内容。

仅供参考,scala 2.12的一个错误是无法抑制ControlThrowable

import scala.util.control.Breaks
import scala.util.control.ControlThrowable
val b = new Breaks

b.breakable {
  try {
    for (num <- 1 to 10) {
      num match {
        case 5 => throw new RuntimeException("5")
        case 6 => b.break
        case x => println(x)
      }
    }
  } catch {
     case c: Throwable => println(c)
  }
}

此代码应最多打印10个数字,因为b.break产生ControlThrowable,并且在b.breakable得到它之前被抑制。但是由于该错误,它最多可以打印5张。

因此,使用scala 2.12时,无论如何都会传播ControlThrowable。它不需要行case c: ControlThrowable => throw c // propagate。但是出于迁移目的,您不应该这样写。

实际上,此错误将在2.13.x https://github.com/scala/scala/pull/7413

中修复。