假设我有一个对象A
,它可能有一个Option
B
,它也可能引用另一个可选对象C
(嵌套选项)
我看到了几种访问C
的方法,比如用于理解、模式匹配或flatMap
,它们看起来都很好。但是,如果我必须返回某种错误消息,或者在B
或C
丢失时抛出异常,或者提供类似cannot execute because B is missing
或C not set
的消息,该怎么办?我只能回到(嘲笑):
if(a.b.isDefined)
{
if(a.b.c.isDefined)
{
//do work
}
else throw new Exception1 //or call something other
}
else throw new OtherException //or do something other
我如何才能以一种功能性或更流畅的方式处理?
您可以添加一个方法将Option
转换为Try
,如果值为none,则会失败,例如
implicit class OptionWrapper[T](val o: Option[T]) extends AnyVal {
def tryGet(e: => Throwable): Try[T] = o match {
case Some(x) => Success(x)
case None => Failure(e)
}
}
然后你可以做
a.b.tryGet(new OtherException).flatMap(_.tryGet(new Exception1))
您可以使用getOrElse[B >: A](default: ⇒ B): B
:
a.b.getOrElse(throw new OtherException).c.getOrElse(throw new Exception1)
使用implicit
包装器和AnyVal
优化(如上所述)是个好主意,但代码的可读性会降低。我认为最好使用现有的方法,而不是每次都编写包装器。
关于map
,它在这里真的没用。如果你不需要投掷Exception
,这是有道理的。
与其将Option转换为其他内容,不如这样做:
def getC(a: A) = a.b.map { b1 =>
b1.c.getOrElse {
throw new UndefinedC
}
}.getOrElse {
throw new UndefinedB
}
简而言之,如果定义了b,那么map将允许您使用它,然后您可以在c上使用getOrElse来返回它,否则抛出异常。最后一个getOrElse应用于b,因此如果map返回c的实例,它将返回它;否则,如果b未定义,它将允许您抛出异常。
我用Scalatest写了一个快速测试来证明这一点:
import org.scalatest._
class TestClass extends FlatSpec with Matchers with OptionValues with Inside with Inspectors {
case class C( name: String ) {
def echo = name
}
case class B( c: Option[C] )
case class A( b: Option[B] )
class UndefinedB extends Exception
class UndefinedC extends Exception
def getC( a: A ) = {
a.b.map { b1 =>
b1.c.getOrElse {
throw new UndefinedC
}
}.getOrElse {
throw new UndefinedB
}
}
"With both b and c defined c.echo" should "be equal to the c.name value" in {
val c = new C( name = "Alessandro" )
val b = new B( Some( c ) )
val a = new A( Some( b ) )
assert( getC(a).echo.equals( "Alessandro" ) )
}
"With only b defined" should "throw UndefinedC" in {
val c = new C( name = "Alessandro" )
val b = new B( None )
val a = new A( Some( b ) )
intercept[UndefinedC] { getC(a).echo.equals( "Alessandro" ) }
}
"With only b not defined" should "throw UndefinedB" in {
val c = new C( name = "Alessandro" )
val b = new B( Some( c ) )
val a = new A( None )
intercept[UndefinedB] { getC(a).echo.equals( "Alessandro" ) }
}
}
编辑:顺便说一句,Alexandr Dorokhin的答案比我的少,如果你在测试中使用它,它们就会通过