seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

scala.util.control.Exception._を使ったサンプル集

サンプル

以下のサンプルはscala.util.control.Exception._がimportされていることを前提にしています。

import scala.util.control.Exception._
allCatch
val result = try {
  throw new Exception 
} catch {
  case e => 
}

まず単にallCatchをつけるだけだと何も変わりません。例外はそのままthrowされてしまいます。

val result = allCatch { 
  throw new Exception 
} // java.lang.Exception

catchして何かするためにはwithApplyにThrowableを受け取って何かする関数を渡します。

allCatch withApply { 
  (t: Throwable) => println("catched") 
} apply { 
  println("foo") 
  throw new Exception 
}
// foo
// catched

val catchingEx = allCatch withApply { t => println("catched") }
catchingEx { 
  println("foo") 
  throw new Exception 
}
// foo
// catched

結果を返す場合はwithApplyに渡す関数で同じ型の結果を返します。

val result: String = allCatch withApply { t => "bar" } apply { "foo" } // "foo"
val result: String = allCatch withApply { t => "bar" } apply { throw new Exception } // "bar"

Option型を返す場合は

val result: Option[String] = allCatch withApply { e => None } apply { Some("foo") } // Some("foo")
val result: Option[String] = allCatch withApply { e => None } apply { throw new Exception } // None

・・ではなく、Catch#opt(T)でOption[T]を返します。

val result: Option[String] = allCatch opt { "foo" } // Some("foo")
val result: Option[String] = allCatch opt { throw new Exception } // None

Catch#either(T)でEither[Throwable, T]を返します。

val result: Either[Throwable, String] = allCatch either { "foo" } // Right(foo)
val result: Either[Throwable, String] = allCatch either { throw new Exception } // Left(java.lang.Exception)

result match { 
  case Left(t) => println("What's wrong?") // t: Throwable
  case Right(result) => println(result) // result: String
}

finallyの処理は・・

val result = try {
  throw new Exception 
} catch {
  case e => 
} finally {
  println("finally")
}

andFinallyで追加します。

val result = allCatch withApply { e => } andFinally { println("finally") } apply { "ok" }

val exHandler = (t: Throwable) => "ng"
val finallyExec = () => println("finally")
val result = allCatch withApply exHandler andFinally finallyExec() apply { "ok" }

ignoring

最初の例のように例外を無視する場合はignoringがあります。

val result = try {
  throw new Exception 
} catch {
  case e => 
}
ignoring(classOf[Throwable]) { throw new Exception }

catching

個別の例外を処理する場合はcatchingに例外クラスを指定します。

val result = try {
  throw new IllegalArgumentException
} catch {
  case e: IllegalArgumentException => "IAE"
  case e => throw e
} // IAE
val catchingEx: Catch[String] = catching(classOf[IllegalArgumentException]) withApply { t => "IAE" } 
val result = catchingEx { "ok" } // ok
val result = catchingEx { throw new IllegalArgumentException } // "IAE"
val result = catchingEx { throw new Exception } // java.lang.Exception

連続パラメータになっているので、複数の例外クラスをまとめることもできます。

val result = try {
  throw new IllegalArgumentException
} catch {
  case e: IllegalArgumentException => "something wrong"
  case e: IllegalStateException => "something wrong"
  case e => throw e
} // IAE
val exHandler = (t: Throwable) => "something wrong"  
val catchingEx = catching(classOf[IllegalArgumentException], classOf[IllegalStateException]) withApply exHandler
val result = catchingEx { throw new IllegalArgumentException } // "something wrong"
val result = catchingEx { throw new IllegalStateException } // "something wrong"

各例外で違うことをやりたい場合は、それぞれで関数をwithApplyで渡して、orで合成します。

val result = try {
  throw new IllegalArgumentException
} catch {
  case e: IllegalArgumentException => "IAE"
  case e: IllegalStateException => "ISE"
  case e => throw e
} // IAE
val catchingIAE = catching(classOf[IllegalArgumentException]) withApply { t => "IAE" }
val catchingISE = catching(classOf[IllegalStateException]) withApply { t => "ISE" }
val catchingEx = catchingIAE or catchingISE 
catchingEx { throw new IllegalArgumentException } // "IAE"
catchingEx { throw new IllegalStateException } // "ISE"
catchingEx { throw new Exception } // java.lang.Exception

catchingPromiscuously

http://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src/library/scala/util/control/Exception.scala#L42

scala.util.control.ControlThrowableとjava.lang.InterruptedExceptionについてはcatchingでは強制的にrethrowされてしまうので、この二つをcatchしたい場合はcatchingPromiscuouslyを使います。

scala> catching(classOf[InterruptedException]) withApply { t => } { throw new InterruptedException }
java.lang.InterruptedException
	at $anonfun$2.apply(<console>:12)
	at $anonfun$2.apply(<console>:12)
	at scala.util.control.Exception$Catch.apply(Exception.scala:88)

scala> catchingPromiscuously(classOf[InterruptedException]) withApply { t => } { throw new InterruptedException }

handling

withApplyではなくbyで例外時の実行関数を渡します。やっていることはcatchingと同じです。

ソースを読むと内部でcatchingでwithApplyしていることがわかります。
http://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_9_1_final/src/library/scala/util/control/Exception.scala#L203

val result = try {
  throw new IllegalArgumentException
} catch {
  case e: IllegalArgumentException => "IAE"
  case e: IllegalStateException => "ISE"
  case e => throw e
} // IAE
val handlingIAE = handling(classOf[IllegalArgumentException]) by { t => "IAE" }
val handlingISE = handling(classOf[IllegalStateException]) by { t => "ISE" }
val handlingEx = handlingIAE or handlingISE
val result = handlingEx { throw new IllegalArgumentException } // IAE
val result = handlingEx { throw new IllegalStateException } // ISE

failing

optと似ていますが、正常系の結果が勝手にSomeにくるまれないので自分でSomeにして返します。

val iaeFailing = failing(classOf[IllegalArgumentException]) 
val result = iaeFailing { Some("foo") } // Some("foo")
val result = iaeFailing { throw new IllegalArgumentException } // None

failAsValue

failAsValueはカリー化された関数で、例外の指定の後にcatchされた後に返す値を名前渡しで指定します。

val iaeFailAsValue = failAsValue(classOf[IllegalArgumentException])("bar")
val result = iaeFailAsValue { "foo" } // "foo"
val result = iaeFailAsValue { throw new IllegalArgumentException } // "bar"

ultimately

お手軽にfinallyの処理だけ追加したい場合に使えます。例外をcatchする処理は書けません。

try {
  println("foo")
} finally {
  println("finally")
}
val withFinally = ultimately { println("finally") } 
withFinally { println("foo") }
// foo
// finally

unwrapping

代わりにThrowable#getCauseして取得した例外を投げます。

unwrapping(classOf[RuntimeException]) { 
  throw new RuntimeException(new IllegalArgumentException) 
} // java.lang.IllegalArgumentException

ただし、withApplyで指定した関数の引数に渡ってくるのはラップされた例外です。

val catchingEx = unwrapping(classOf[RuntimeException]) withApply { 
  e => e.printStackTrace // java.lang.RuntimeException
}
catchingEx { 
  throw new RuntimeException(new IllegalArgumentException) 
}