3

我有许多嵌套对象,都包含在 Scala Option 类型中。在我的项目的其他地方,我不得不调用一个嵌入了大约 5 个级别(其中一些是列表)的属性,每次调用.get. 这样,我最终得到如下所示的东西:

objectA.get.attrB.get.attrC.get(0).attrD.get

除了一系列.get调用(我不确定这是否理想)之外,我并没有以这种方式实现太多错误处理,如果任何属性为空,那么整个事情就会崩溃。鉴于嵌套调用,如果我将其限制为如上所述的单行,我也只能.getOrElse在最后使用一次。

在 Scala 中是否有任何使用选项类型的建议方法?

4

2 回答 2

6

在这种情况下,开箱即用的最易读的解决方案(即,不编写辅助方法)可能是链式调用Option.flatMap

objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)

通过使用flatMap,如果链中的任何选项是None,您最终会None得到 (没有例外,不像getwhich 在调用时会爆炸None)。

举例:

case class C(attrD: Option[String])
case class B(attrC: List[C])
case class A(attrB: Option[B])

val objectA = Some(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))

// returns Some(foo)
objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD) 

val objectA2 = Some(A(Some(B(List()))))

// returns None
objectA2 flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)

您也可以使用 for 理解而不是(用于理解被脱糖到/flatMap的链),但在这种情况下,它实际上可读性较差(通常情况相反),因为在每一步您都必须引入一个绑定,然后在下一步中参考:flatMapmap

for ( a <- objectA; b <- a.attrB; c <- b.attrC.headOption; d <- c.attrD ) yield d

如果您愿意使用 ScalaZ,另一种解决方案是使用>>=代替flatMap,这使它更短:

val objectA = Option(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))
// returns Some(foo)
objectA >>= (_.attrB) >>= (_.attrC.headOption) >>= (_.attrD)

这实际上与 using 完全相同flatMap,只是更短。

于 2015-02-20T10:21:01.413 回答
2

我相信你想要这个,

val deepNestedVal = objectA.get.attrB.get.attrC.get.attrD.get

我认为更优选的方法是使用理解,

val deepNestedVal = for {
  val1 <- objectA
  val2 <- val1.attrB
  val3 <- val2.attrC
  val4 <- val3.attrD
} yield val4

其他方法是使用flatMap@Régis Jean-Gilles 的答案所示

于 2015-02-20T10:31:17.937 回答