[Scala] Future#recoverを使って例外処理する

  #scala

環境

➜  ~  scala
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11).
Type in expressions to have them evaluated.
Type :help for more information.

やりたいこと

scala.concurrent.Futureで例外が発生してFailureになったとき、

といったことをしたい。

recoverを使う

そんなときのためのFuture#recover
Throwableをキャッチして新しいFutureを作ってくれる。

シグネチャは以下。

def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U]

ソースはこの辺

下準備

scala> import scala.concurrent.Future
import scala.concurrent.Future

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> case class MyException() extends Exception
defined class MyException

サンプル

// まずはそのまま実行してみる
scala> Future {
     |   throw new IllegalArgumentException()
     | }
res0: scala.concurrent.Future[Nothing] = scala.concurrent.impl.Promise$DefaultPromise@b59d31

scala> res0.value.get
res1: scala.util.Try[Nothing] = Failure(java.lang.IllegalArgumentException)


// デフォルト値を適用したい!
scala> Future {
     |   throw new IllegalArgumentException()
     | } recover {
     |   case e: IllegalArgumentException => 0
     | }
res2: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@2e1d27ba

scala> res2.value.get
res3: scala.util.Try[Int] = Success(0)


// 別の例外を投げたい!
scala> Future {
     |   throw new IllegalArgumentException()
     | } recover {
     |   case e: IllegalArgumentException => throw MyException()
     | }
res4: scala.concurrent.Future[Nothing] = scala.concurrent.impl.Promise$DefaultPromise@3bcd05cb

scala> res4.value.get
res5: scala.util.Try[Nothing] = Failure(MyException)


// Successなときはそのままの結果
scala> Future {
     |   10
     | } recover {
     |   case e: IllegalArgumentException => throw MyException()
     | }
res6: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@56928307

scala> res6.value.get
res7: scala.util.Try[Int] = Success(10)

recoverWithを使う

Future#recoverWithなんていうのもある。
Future#recoverとは部分関数の型が違って、Throwableを受け取ってFutureを返すようになる。

シグネチャは以下。

def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U]

ソースはこの辺

サンプル

// デフォルト値を適用したい!
scala> Future {
     |   throw new IllegalArgumentException()
     | } recoverWith {
     |   case e: IllegalArgumentException => Future.successful(0)
     | }
res8: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@463b4ac8

scala> res8.value.get
res9: scala.util.Try[Int] = Success(0)


// 別の例外を投げたい!
// これは特に変わらず
scala> Future {
     |   throw new IllegalArgumentException()
     | } recover {
     |   case e: IllegalArgumentException => throw MyException()
     | }
res10: scala.concurrent.Future[Nothing] = scala.concurrent.impl.Promise$DefaultPromise@4a3e3e8b

scala> res10.value.get
res11: scala.util.Try[Nothing] = Failure(MyException)


// Successなときはそのままの結果
scala> Future {
     |   10
     | } recoverWith {
     |   case e: IllegalArgumentException => Future.successful(0)
     | }
res12: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@6a1ebcff

scala> res12.value.get
res13: scala.util.Try[Int] = Success(10)