我们在 Scala 中有抑制异常吗?
Do we have Suppressed Exception in Scala?
在 Scala 中,我们可以将 finally 块中的异常抛出作为抑制异常添加回原始异常吗?
在Java中,如果我们在 try 块中遇到异常,然后在 finally 块中遇到另一个异常,则第二个异常将作为被抑制的异常添加回第一个异常。因此,第二个异常不会掩盖第一个异常,我们仍然可以通过检查其抑制的异常来分析发生了什么。
import java.util.stream.Stream;
class Scratch {
static class MyCloseable implements AutoCloseable {
@Override
public void close() throws Exception {
throw new Exception("FROM CLOSE METHOD");
}
}
public static void main(String[] args) {
try {
try (final MyCloseable closeable = new MyCloseable()){
throw new Exception("From Try Block");
}
} catch (Throwable t) {
System.out.println(t);
Stream.of(t.getSuppressed()).forEach(System.out::println);
}
}
}
会抛出异常
java.lang.Exception: From Try Block
java.lang.Exception: FROM CLOSE METHOD
然而,Scala 似乎只是简单地重新抛出第二个异常(从 finally 块中抛出)并忽略第一个异常(从 try 块中抛出)。
try {
try {
throw new Exception("From Try Block")
} finally {
throw new Exception("From Final Block")
}
} catch {
case e => e :: e.getSuppressed().toList
}
上面的代码将简单地 return 只有第二个异常(从 final 块中抛出)。但是,我想有一种方法可以同时获得两个异常。
如何以更好的方式编写上述代码?
由于 Scala 不支持 java 的 try-with-resources 构造,我想有一个明显的方法可以使上面的代码更好地 = 记住第一个异常:
try {
var te: Option[Throwable] = None
try {
throw new Exception("From Try Block")
} catch {
case t: Throwable =>
te = Some(t)
throw t
} finally {
try {
throw new Exception("From Final Block")
} catch {
case fe: Throwable =>
throw te.map { e => e.addSuppressed(fe); e }.getOrElse(fe)
}
}
} catch {
case e: Throwable =>
(e :: e.getSuppressed().toList).foreach(println)
}
输出:
java.lang.Exception: From Try Block
java.lang.Exception: From Final Block
下面是我会在实际项目中使用的更好的方法:
def withResources[T <: AutoCloseable, V](r: => T)(f: T => V): V = {
val resource: T = r
var e: Option[Throwable] = None
try f(resource)
catch {
case t: Throwable =>
e = Some(t)
throw t
} finally e.fold(resource.close())(te =>
try resource.close()
catch {
case t: Throwable =>
te.addSuppressed(t)
throw te
})
}
val resource = new AutoCloseable {
override def close(): Unit = throw new Exception("From Final Block")
}
try {
withResources(resource)(_ => throw new Exception("From Try Block"))
} catch {
case e: Throwable =>
(e :: e.getSuppressed().toList).foreach(println)
}
输出:
java.lang.Exception: From Try Block
java.lang.Exception: From Final Block
在 Scala 中,我们可以将 finally 块中的异常抛出作为抑制异常添加回原始异常吗?
在Java中,如果我们在 try 块中遇到异常,然后在 finally 块中遇到另一个异常,则第二个异常将作为被抑制的异常添加回第一个异常。因此,第二个异常不会掩盖第一个异常,我们仍然可以通过检查其抑制的异常来分析发生了什么。
import java.util.stream.Stream;
class Scratch {
static class MyCloseable implements AutoCloseable {
@Override
public void close() throws Exception {
throw new Exception("FROM CLOSE METHOD");
}
}
public static void main(String[] args) {
try {
try (final MyCloseable closeable = new MyCloseable()){
throw new Exception("From Try Block");
}
} catch (Throwable t) {
System.out.println(t);
Stream.of(t.getSuppressed()).forEach(System.out::println);
}
}
}
会抛出异常
java.lang.Exception: From Try Block
java.lang.Exception: FROM CLOSE METHOD
然而,Scala 似乎只是简单地重新抛出第二个异常(从 finally 块中抛出)并忽略第一个异常(从 try 块中抛出)。
try {
try {
throw new Exception("From Try Block")
} finally {
throw new Exception("From Final Block")
}
} catch {
case e => e :: e.getSuppressed().toList
}
上面的代码将简单地 return 只有第二个异常(从 final 块中抛出)。但是,我想有一种方法可以同时获得两个异常。
如何以更好的方式编写上述代码?
由于 Scala 不支持 java 的 try-with-resources 构造,我想有一个明显的方法可以使上面的代码更好地 = 记住第一个异常:
try {
var te: Option[Throwable] = None
try {
throw new Exception("From Try Block")
} catch {
case t: Throwable =>
te = Some(t)
throw t
} finally {
try {
throw new Exception("From Final Block")
} catch {
case fe: Throwable =>
throw te.map { e => e.addSuppressed(fe); e }.getOrElse(fe)
}
}
} catch {
case e: Throwable =>
(e :: e.getSuppressed().toList).foreach(println)
}
输出:
java.lang.Exception: From Try Block
java.lang.Exception: From Final Block
下面是我会在实际项目中使用的更好的方法:
def withResources[T <: AutoCloseable, V](r: => T)(f: T => V): V = {
val resource: T = r
var e: Option[Throwable] = None
try f(resource)
catch {
case t: Throwable =>
e = Some(t)
throw t
} finally e.fold(resource.close())(te =>
try resource.close()
catch {
case t: Throwable =>
te.addSuppressed(t)
throw te
})
}
val resource = new AutoCloseable {
override def close(): Unit = throw new Exception("From Final Block")
}
try {
withResources(resource)(_ => throw new Exception("From Try Block"))
} catch {
case e: Throwable =>
(e :: e.getSuppressed().toList).foreach(println)
}
输出:
java.lang.Exception: From Try Block
java.lang.Exception: From Final Block