Matching on nested exception type

0__'s answer is good, but it would be better if you were not forced to write a specific object (CausedByFoo) for each potential exception. As it happens, there is not much to change to end up with a generic CausedBy helper object:

class Foo(message: String) extends Exception(message)
class Bar(cause: Throwable) extends Exception(cause)

object CausedBy {
  def unapply(e: Throwable): Option[Throwable] = Option(e.getCause)
}

def test(block: => Unit): String = 
  try {
    block
    "ok"
  } catch {
    case CausedBy(ex: Foo) => "not ok: " + ex.getMessage
  }

test(println("hello"))
test(println("hello".toInt)) // uncaught exception
test(throw new Bar(new Foo("Ooops, foo error!"))) // caught

As should be obvious, you can use CausedBy with any exception (by example by doing case CausedBy(ex: Baz).

You can even nest it to handle an exception caused by an exception caused by an exception (by doing something like case CausedBy(CausedBy(ex: Foo))


The catch block can handle any regular pattern match, so

class Foo extends Exception
class Bar(cause: Exception) extends Exception(cause)

object CausedByFoo {
  def unapply(e: Exception): Boolean = e.getCause match {
    case _: Foo => true
    case _ => false
  }
}

def test(block: => Unit): String = 
  try {
    block
    "ok"
  } catch {
    case CausedByFoo() => "not ok"
  }

test(println("hello"))
test(println("hello".toInt)) // uncaught exception
test(throw new Bar(new Foo)) // caught

Tags:

Scala