我有很多嵌套对象,都包含在Scala Option类型中。在我的项目的其他地方,我不得不调用一个嵌入了5级深度的属性(其中一些是列表),每次调用.get
。这样我最终会看到如下内容:
objectA.get.attrB.get.attrC.get(0).attrD.get
除了一系列.get
调用(我不确定是理想的)之外,我没有用这种方式实现很多错误处理,如果任何属性都是空的,那么整个事情就会崩溃。给定嵌套调用,如果我将其限制为如上所述的单行,我最后也只能使用.getOrElse
一次。
Scala中是否有任何建议的使用Option类型的方法?
答案 0 :(得分:6)
在这种情况下,开箱即用的最易读的解决方案(即没有编写帮助方法)可能会将调用链接到Option.flatMap
:
objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
使用flatMap
,如果链中的任何选项为None
,您最终会以None
结尾(与{{1不同)在get
)上调用时会被打击。
以示例:
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)
(因为理解被贬低到flatMap
/ flatMap
的链),但在这种情况下它实际上会更不易读(相反通常是正确的)因为在每一步中你都必须引入一个绑定,然后在下一步中引用它:
map
如果您愿意使用ScalaZ,另一种解决方案是使用for ( a <- objectA; b <- a.attrB; c <- b.attrC.headOption; d <- c.attrD ) yield d
代替>>=
,这会缩短一些时间:
flatMap
这与使用val objectA = Option(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))
// returns Some(foo)
objectA >>= (_.attrB) >>= (_.attrC.headOption) >>= (_.attrD)
完全相同,只是更短。
答案 1 :(得分:2)
我相信你想要这个,
val deepNestedVal = objectA.get.attrB.get.attrC.get.attrD.get
我认为更优选的方式是使用for -reherehe,
val deepNestedVal = for {
val1 <- objectA
val2 <- val1.attrB
val3 <- val2.attrC
val4 <- val3.attrD
} yield val4
其他方式是使用flatMap
,如@RégisJean-Gilles的回答所示