#1.とっても便利なTry
Scalaを使っていて、以下のような書き方をしてしまうことあるのでは無いでしょうか?
def sampleDBAccess(id:String):writeEntity = {
try{
//何か転けそうなselect
DBAccessor.selectById(id)
}catch{
case notfound:EntityNotFoundException => Entity.empty
case e:Throwable => throw e
}
}
JavaとかCとかやってるとつい書いてしまう処理です。
これだと、Exception投げる処理が分散してしまって、どこでなげたっけ?あれ??となってしまったりします。
また、関数型的にかっこよくないなーって感じるかもしれません。
そこで!Scalaのscala.util.Tryです!
def sampleDBAccess(id:String):Try[writeEntity] = Try {
DBAccessor.selectById(id).recover {case notfound:EntityNotFoundException => Success(Entity.empty) }
}
こんな感じ。
TryはSuccess()とFailure()に分かれていて、こけたらFailureに勝手にExceptionいれてくれる優れものです。
#2.実際にどうやって使うの?
サンプルを使って、実際に書いてみます。
##2-1. 参照したデータを使う。
// ユーザー名からEntityを引っ張ってくる関数
def resolveByName(name:String):Try[User]
// 使い方
val ids = resolveByName("yusuke").map(_.id) // Try[id]がかえる。
// キャッチする場所
val ids = ids.recover({
//特定のExceptionの場合復帰させるのであればここに書く
case e:Throwable => throw e
}).get
2-2. 参照したデータでさらに参照してみる。
// ユーザー名からEntityを引っ張ってくる関数
def resolveByName(name:String):Try[User]
def resolveByUserId(id:UserId):Try[UserData]
// 使い方
val ids = resolveByName("yusuke").flatMap(user => resolveByUserId(user.id)) // Try[UserData]がかえる。
Try[Try[xxxx]]となった場合にはSeq同様にFlatMapを使って1つにまとめることができます。
これを十分活用すれば、Exceptionをまとめて上の層に渡すことも可能です。
2-3 こけてもこけてないように見せたい
// ユーザー名からEntityを引っ張ってくる関数
def resolveByName(name:String):Try[User]
// 上記でもかきましたがgetOrElseでもみ消しましょう。
resolveByName("hoge").getOrElse(/*デフォルト値*/)
2−4 複数のTryを扱う。
def resolveByName(name:String):Try[User]
def createByUserId(id:UserId):Try[Message]
val message = for{
u <- resolveByName("hoge")
message <- createByUserId(u.id)
} yield message
//messageにTry[Message]がはいる。
#3. 最後に
このような感じで、scala.util.Tryはコケても、アプリケーションまでコケさせなくしたり、任意にコケさせたりできます。
いままで、try-catchで書いていた人もでscala.util.Tryを使ってスッキリさせませんか?