如何在scala中编写一个正确的null安全合并运算符?

时间:2009-09-01 20:01:22

标签: scala coalescing

看到像this one之类的问题的答案涉及恐怖表演,比如试图抓住NPE并将错位的名称从堆栈轨迹中挖出来,我问这个问题所以我可以回答它。

欢迎评论或进一步改进。

2 个答案:

答案 0 :(得分:36)

像这样:

case class ?:[T](x: T) {
  def apply(): T = x
  def apply[U >: Null](f: T => U): ?:[U] =
    if (x == null) ?:[U](null)
    else ?:[U](f(x))
}

行动中:

scala> val x = ?:("hel")(_ + "lo ")(_ * 2)(_ + "world")()
x: java.lang.String = hello hello world

scala> val x = ?:("hel")(_ + "lo ")(_ => (null: String))(_ + "world")()
x: java.lang.String = null

答案 1 :(得分:0)

添加了orElse

case class ?:[T](x: T) {
  def apply(): T = x
  def apply[U >: Null](f: T => U): ?:[U] =
    if (x == null) ?:[U](null)
    else ?:[U](f(x))
  def orElse(y: T): T = 
    if (x == null) y
    else x
}
scala> val x = ?:(obj)(_.subField)(_.subSubField).orElse("not found")
x: java.lang.String = not found

或者如果您更喜欢命名语法而不是运算符语法

case class CoalesceNull[T](x: T) {
  def apply(): T = x
  def apply[U >: Null](f: T => U): CoalesceNull[U] =
    if (x == null) CoalesceNull[U](null)
    else CoalesceNull[U](f(x))
  def orElse(y: T): T =
    if (x == null) y
    else x
}
scala> val x = CoalesceNull(obj)(_.subField)(_.subSubField).orElse("not found")
x: java.lang.String = not found

更多例子

case class Obj[T](field: T)

  test("last null") {
    val obj: Obj[Obj[Obj[Obj[String]]]] = Obj(Obj(Obj(Obj(null))))
    val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
    res0 should === (null)
    val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
    res1 should === ("not found")
  }

  test("first null") {
    val obj: Obj[Obj[Obj[Obj[String]]]] = null
    val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
    res0 should === (null)
    val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
    res1 should === ("not found")
  }

  test("middle null") {
    val obj: Obj[Obj[Obj[Obj[String]]]] = Obj(Obj(null))
    val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
    res0 should === (null)
    val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
    res1 should === ("not found")
  }

  test("not null") {
    val obj: Obj[Obj[Obj[Obj[String]]]] = Obj(Obj(Obj(Obj("something"))))
    val res0 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field)()
    res0 should === ("something")
    val res1 = CoalesceNull(obj)(_.field)(_.field)(_.field)(_.field).orElse("not found")
    res1 should === ("something")
  }
}