我们在 Scala 中有抑制异常吗?

Posted

技术标签:

【中文标题】我们在 Scala 中有抑制异常吗?【英文标题】:Do we have Suppressed Exception in Scala? 【发布时间】:2019-05-09 17:09:09 【问题描述】:

在 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

上面的代码只会返回第二个异常(从最终块抛出)。但是,我想有一种方法来获得这两个例外。

无论如何要以更好的方式制作上述代码?

【问题讨论】:

现在在 Java 8 中检查:try try throw new Exception("From Try Block"); finally throw new Exception("From Final Block"); catch (Throwable t) System.out.println(t); Stream.of(t.getSuppressed()).forEach(System.out::println); 仅打印 java.lang.Exception: From Final Block the second exception will be added back to the first exception as a suppressed exception 你从哪里得到这个想法?来自JLS 14.20.2(强调我的):“如果 finally 块由于原因 S 突然完成,那么 try 语句由于原因 S 突然完成(并且值 V 的抛出被丢弃并忘记)。” Java 的 try-with-resources 会自动添加抑制的异常。查看这个答案***.com/questions/39866000/… 以了解如何在 Scala 中实现和抽象它。 您好 Dmytro Mitin 和 Silvio Mayolo,感谢您的评论。我指的不是 java try-catch-finally 块,而是 try-with-resources 语句。不知何故,它们的工作方式不同。 嗨@Kolmar,感谢您的链接。看起来很有帮助。让我看看这个。 【参考方案1】:

由于 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 中有抑制异常吗?的主要内容,如果未能解决你的问题,请参考以下文章

addSuppressed异常抑制

addSuppressed异常抑制

检查 Scala 匹配表达式中的两个特定异常

抑制预期 Oracle 异常的 PHP 警告

在Scala中抛出异常,啥是“官方规则”

抑制异常和原因之间的区别