Есть ли у нас исключенные исключения в Scala? - PullRequest
2 голосов
/ 09 мая 2019

Можем ли мы добавить Scal throw в блоке finally к исходному исключению в качестве подавленного исключения в Scala?

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

Приведенный выше код просто вернет только второе исключение (выброшенное из последнего блока).Тем не менее, я хотел бы иметь способ получить оба исключения.

В любом случае, чтобы сделать код выше, лучше?

1 Ответ

3 голосов
/ 11 мая 2019

Поскольку 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
...