首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Scala Option对象中的另一个Option对象

Scala Option对象中的另一个Option对象
EN

Stack Overflow用户
提问于 2013-02-27 19:59:54
回答 4查看 5.2K关注 0票数 12

我有一个模型,它有一些选项字段,其中包含另一个选项字段。例如:

代码语言:javascript
运行
复制
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,这就是这种模型设计的原因。

所以问题是:获得最深领域的最好方法是什么?

代码语言:javascript
运行
复制
First.get.second.get.third.get.numberOfSmth.get

上面的方法看起来真的很难看,如果其中一个对象为None,它可能会导致异常。我正在查看Scalaz lib,但没有找到更好的方法。

有什么想法吗?提前谢谢。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-02-27 20:05:58

解决方案是使用Option.mapOption.flatMap

代码语言:javascript
运行
复制
First.flatMap(_.second.flatMap(_.third.map(_.numberOfSmth)))

或等效项(请参阅本答案末尾的更新):

代码语言:javascript
运行
复制
First flatMap(_.second) flatMap(_.third) map(_.numberOfSmth)

这将返回一个Option[Int] (假设numberOfSmth返回一个Int)。如果调用链中的任何选项为None,则结果将为None,否则将为Some(count),其中countnumberOfSmth返回的值。

当然,这可能很快就会变得丑陋。出于这个原因,scala支持将理解作为一种语法糖。上面的代码可以重写为:

代码语言:javascript
运行
复制
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的扁平链):

代码语言:javascript
运行
复制
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)
票数 20
EN

Stack Overflow用户

发布于 2013-02-27 20:03:38

不需要scalaz:

代码语言:javascript
运行
复制
for { 
  first  <- yourFirst
  second <- f.second
  third  <- second.third
  number <- third.numberOfSmth
} yield number

或者,您可以使用嵌套flatMaps

票数 10
EN

Stack Overflow用户

发布于 2013-02-27 20:03:23

这可以通过将调用链接到flatMap来完成

代码语言:javascript
运行
复制
def getN(first: Option[First]): Option[Int] =
  first flatMap (_.second) flatMap (_.third) flatMap (_.numberOfSmth)

您也可以使用for-comprehension来完成此操作,但它会更加冗长,因为它会强制您命名每个中间值:

代码语言:javascript
运行
复制
def getN(first: Option[First]): Option[Int] =
  for {
    f <- first
    s <- f.second
    t <- s.third
    n <- t.numberOfSmth
  } yield n
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15111565

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档