此示例来自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无论如何都不会被抓住。我在这里想念东西吗?
答案 0 :(得分:3)
这不是没有用的。
考虑一下:
class FooException extends Exception with ControlThrowable
这将被第一个方块捕获并重新抛出,再也不会到达第二个方块。编写这样的异常可能不是一个好主意,但是,当首次引入ControlThrowable时(当时称为ControlException),它就是not uncommon in the standard library。
答案 1 :(得分:2)
我想扩展Brian McCutchon的答案。我认为有几个不同的方面(高度哲学的方面)值得考虑。
有人可能会说,您可以直接从Throwable
继承的原因是因为Java类型系统没有禁止这种扩展的方法,并且Throwable
的层次结构并非旨在添加不是Error
或Exception
的子类的更多类。这可能是诸如NonLocalReturnException
或BreakException
之类的东西最初是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
中修复。