我有一个模型,它有一些选项字段,其中包含另一个选项字段。例如:
case class First(second: Option[Second], name: Option[String])
case class Second(third: Option[Third], title: Option[String])
case class Third(numberOfSmth: Option[Int])
我从外部JSON接收这些数据,有时这些数据可能包含null,这就是这种模型设计的原因。
所以问题是:获得最深领域的最好方法是什么?
First.get.second.get.third.get.numberOfSmth.get
上面的方法看起来真的很难看,如果其中一个对象为None,它可能会导致异常。我正在查看Scalaz lib,但没有找到更好的方法。
有什么想法吗?提前谢谢。
发布于 2013-02-27 12:05:58
解决方案是使用Option.map
和Option.flatMap
First.flatMap(_.second.flatMap(_.third.map(_.numberOfSmth)))
或等效项(请参阅本答案末尾的更新):
First flatMap(_.second) flatMap(_.third) map(_.numberOfSmth)
这将返回一个Option[Int]
(假设numberOfSmth
返回一个Int
)。如果调用链中的任何选项为None
,则结果将为None
,否则将为Some(count)
,其中count
是numberOfSmth
返回的值。
当然,这可能很快就会变得丑陋。出于这个原因,scala支持将理解作为一种语法糖。上面的代码可以重写为:
for {
first <- First
second <- first .second
third <- second.third
} third.numberOfSmth
这无疑更好(特别是如果您还不习惯随处可见map
/flatMap
,在使用scala一段时间后肯定会出现这种情况),并且在幕后生成完全相同的代码。
有关更多背景信息,您可以查看另一个问题:What is Scala's yield?
更新:感谢Ben James指出flatMap是联想的。换句话说,x flatMap(y flatMap z)))
与x flatMap y flatMap z
是相同的。虽然后者通常不短,但它的优点是避免了任何嵌套,这更容易遵循。
下面是REPL中的一些插图(4种样式是等价的,前两种使用flatMap嵌套,另外两种使用flatMap的扁平链):
scala> val l = Some(1,Some(2,Some(3,"aze")))
l: Some[(Int, Some[(Int, Some[(Int, String)])])] = Some((1,Some((2,Some((3,aze))))))
scala> l.flatMap(_._2.flatMap(_._2.map(_._2)))
res22: Option[String] = Some(aze)
scala> l flatMap(_._2 flatMap(_._2 map(_._2)))
res23: Option[String] = Some(aze)
scala> l flatMap(_._2) flatMap(_._2) map(_._2)
res24: Option[String] = Some(aze)
scala> l.flatMap(_._2).flatMap(_._2).map(_._2)
res25: Option[String] = Some(aze)
发布于 2013-02-27 12:03:38
不需要scalaz:
for {
first <- yourFirst
second <- f.second
third <- second.third
number <- third.numberOfSmth
} yield number
或者,您可以使用嵌套flatMaps
发布于 2013-02-27 12:03:23
这可以通过将调用链接到flatMap
来完成
def getN(first: Option[First]): Option[Int] =
first flatMap (_.second) flatMap (_.third) flatMap (_.numberOfSmth)
您也可以使用for-comprehension来完成此操作,但它会更加冗长,因为它会强制您命名每个中间值:
def getN(first: Option[First]): Option[Int] =
for {
f <- first
s <- f.second
t <- s.third
n <- t.numberOfSmth
} yield n
https://stackoverflow.com/questions/15111565
复制