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?

1 Answers

3
Matthew Ivanoff On Best Solutions

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