Search code examples
scalaexceptiontry-with-resources

Do we have Suppressed Exception in Scala?


In Scala, can we add exception throw in a finally block back to the original exception as an suppressed exception?

In Java, if we hit an exception in the try block and then another exception in the finally block, the second exception will be added back to the first exception as a suppressed exception. Therefore, the second exception would not mask the first exception and we still can analyse what happened via checking its suppressed exception.

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);
        }
    }
}

would throw exceptions

  • java.lang.Exception: From Try Block
  • java.lang.Exception: FROM CLOSE METHOD

However, it seems that Scala simply rethrows the second exception (throwing from finally block) and ignore the first exception (throwing from the try block).

try {
  try {
    throw new Exception("From Try Block")
  } finally {
    throw new Exception("From Final Block")
  }
} catch {
  case e => e :: e.getSuppressed().toList
}

The above code will simply return only the second exception (thrown from final block). However, I would like to have a way to get both exception.

Anyway to make above code in a better way?


Solution

  • Inasmuch as Scala does not support java's try-with-resources construct, I suppose there is an obvious way to make the code above in a better way = to memorize the first exception:

        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)
        }
    

    output:

    java.lang.Exception: From Try Block
    java.lang.Exception: From Final Block
    

    And below is a nicer way that I would use in the real project:

      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)
        }
    

    output:

    java.lang.Exception: From Try Block
    java.lang.Exception: From Final Block