嵌套Scala选项字段的最佳实践?

Gro*_*lla 3 scala scala-option

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

objectA.get.attrB.get.attrC.get(0).attrD.get
Run Code Online (Sandbox Code Playgroud)

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

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

Rég*_*les 6

在这种情况下,开箱即用的最易读的解决方案(也就是说,没有编写帮助器方法)可能会将调用链接到Option.flatMap:

objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
Run Code Online (Sandbox Code Playgroud)

通过使用flatMap,如果链中的任何选项是None,你最终会None得到结果(没有例外,与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)
Run Code Online (Sandbox Code Playgroud)

你也可以用于理解而不是flatMap(因为理解被贬低到flatMap/的链map),但在这种情况下它实际上会更不可读(相反通常是正确的),因为在每一步你必须引入一个绑定然后参考下一步:

for ( a <- objectA; b <- a.attrB; c <- b.attrC.headOption; d <- c.attrD ) yield d
Run Code Online (Sandbox Code Playgroud)

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

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

这与使用完全一样flatMap,只是更短.