Два исключения «в одно и то же время», как правильно разрешить эту ситуацию? - PullRequest
0 голосов
/ 15 сентября 2018

У меня есть ситуация, когда мне нужно вызвать метод foo, если он не работает, вызвать метод bar, а затем повторно выдать исходное исключение (обернутое).

Моя проблема в том, что метод bar тоже может завершиться ошибкой, и информация об исключениях также важна.

Когда я создаю новое Исключение, у меня только одна причина, а у меня две. Я мог бы создать что-то вроде class MyException(cause1, cause2) extends RuntimeException(cause1), но cause2 будет вне стандартных механизмов, которые имеют исключения (отслеживание стека и т. Д.).

Есть ли лучший способ справиться с этой ситуацией?

Следующий код является упрощенным примером.

// I can't change this code
def foo:String = throw new FooException("foo method fails")
def bar:String = throw new BarException("bar method fails")

// my code
try {
  foo
} catch {
  case error1:FooException =>
    try {
      // if foo fails, I want to call bar
      bar
      throw new MyException("my exception", error1)
    } catch {
      case error2:BarException =>
        // but bar could fail too
        throw new MyException("my exception", ???)
        // if my cause is error1, I lost error2 information (message and stack)
        // if my cause is error2, I lost error1 information (message and stack)
    }
}

Заранее спасибо.

1 Ответ

0 голосов
/ 15 сентября 2018

Вы можете упаковать свой MyException с одним или обоими «подавленными» исключениями.

val myEx = new MyException("my exception")
myEx.addSuppressed(error1)
myEx.addSuppressed(error2)
throw myEx

Тогда нужно перехватить код, чтобы распаковать его.

case err : MyException =>
  val arr :Array[Throwable] = err.getSuppressed
  // arr contains all the "suppressed" exceptions that were added,
  // stack traces and all

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

import util.Try

val res :Either[List[Throwable],String] =
  Try(foo).fold(fErr =>
    Try(bar).fold(bErr => Left(fErr::bErr::Nil), _ => Left(fErr::Nil))
  , Right(_))

Оттуда вы можете продолжить по мере необходимости.

res match {
  case Right(s)  => println(s)  //foo result string
  case Left(lst) =>
    println(lst.map(_.getMessage()).mkString(",")) //foo method fails,bar method fails
    lst.foreach(_.printStackTrace())               //both stack traces
}
...