【问题标题】:Scala Monad - without for statement(elegant way to check null attribute)Scala Monad - 没有 for 语句(检查空属性的优雅方法)
【发布时间】:2018-07-12 14:06:14
【问题描述】:

假设我只有一个顶级对象,另外两个对象嵌套在里面作为属性:

class Human(val name: String, val child: Human)

val anita = new Human("Anita", null)
val david = new Human("David", anita)
val fabrizio = new Human("Fabrizio", david)

如果我必须让最后一个孩子的名字开始 fabrizio,我需要做很多空检查控制:

if(fabrizio != null)
    if(fabrizio.child!= null)
        if(fabrizio.children.child!= null)
            println(fabrizio.child.child.name)

我在 scala 上发现了这个“语法糖”,但它似乎只适用于集合:

println( for(x <- fabrizio.child; y <- x.child; z <- y.child) yield z.name)

事实上它在抱怨:

value flatMap is not a member of Playground.this.Human

有没有办法在不将顶级对象放入集合中的情况下获取最后一个孩子的名字?

【问题讨论】:

    标签: java scala monads


    【解决方案1】:

    它不是收藏品独有的。 Scalas for-comprehension desugars 嵌套flatMap 调用和最终map。不要在代码中使用null,而是使用Option[T] 来表达Human 及其子代可能会或可能不会被定义的事实:

    final case class Human(name: String, children: Option[Human])
    val maybeAnita: Option[Human] = Some(Human("Anita", None))
    val maybeDavid: Option[Human] = Some(Human("David", maybeAnita))
    val maybeFabrizio: Option[Human] = Some(Human("Fabrizio", maybeDavid))
    

    现在你可以:

    val maybeName: Option[String] = for {
      fabrizio <- maybeFabrizio
      children <- fabrizio.children
      nestedChildren <- children.children
    } yield nestedChildren.name
    

    【讨论】:

      【解决方案2】:

      Yuval 建议使用Option 是处理null 的惯用且简单的方式,是一个极好的建议。我想补充一点:

      如果您正在寻找最后一个孩子的名字,您可以简单地创建一个函数:

      def lastChild(person: Human, isChild: Boolean = false): Option[Human] = {
          person.children match {
              case Some(child) => lastChild(child, true)
              case None if isChild => Some(person)
              case _ => None
          }
      }
      

      您还可以添加一个级别变量来确保某一代:

      def nthChild(person: Human, level: Int, isChild: Boolean = false): Option[Human] = {
          person.children match {
              case Some(child) if (level > 0) => lastChild(child, (level - 1), true)
              case _ if ((level == 0) && isChild) => Some(person)
              case _ => None
          }
      }
      

      附带说明:如果每个 Human 只能有一个孩子,则该字段应命名为 child 以清楚起见

      【讨论】:

        猜你喜欢
        • 2011-03-28
        • 1970-01-01
        • 1970-01-01
        • 2015-07-14
        • 1970-01-01
        • 2016-12-01
        • 2011-02-05
        • 1970-01-01
        • 2014-03-05
        相关资源
        最近更新 更多