2

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?

William Wong
  • 319
  • 3
  • 12
  • Checked now in 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); }` prints only `java.lang.Exception: From Final Block`. – Dmytro Mitin May 09 '19 at 17:16
  • 1
    `the second exception will be added back to the first exception as a suppressed exception` Where do you get this idea? From [JLS 14.20.2](https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.2) (emphasis mine): "If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (**and the throw of value V is discarded and forgotten**)." – Silvio Mayolo May 09 '19 at 17:16
  • 2
    Java's try-with-resources adds suppressed exceptions automatically. Look in this answer https://stackoverflow.com/questions/39866000/java-try-with-resource-not-working-with-scala to see how to implement and abstract it in Scala. – Kolmar May 09 '19 at 17:19
  • Hi Dmytro Mitin and Silvio Mayolo, Thanks for your comment. I am not referring to the java try-catch-finally block, but the try-with-resources statement. Somehow, they do not work in the same way. – William Wong May 10 '19 at 15:30
  • Hi @DmytroMitin and Silvio Mayolo I added a java example to illustrate what I would like to achieve with Scala. – William Wong May 10 '19 at 15:42
  • 1
    Hi @Kolmar, Thanks for the link. It looks helpful. Let me check this out. – William Wong May 10 '19 at 15:48

1 Answers1

3

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
Matthew I.
  • 1,793
  • 2
  • 10
  • 21