24

Having seen the answers coming out of questions like this one involving horror shows like trying to catch the NPE and dredge the mangled name out of the stack trace, I am asking this question so I can answer it.

Comments or further improvements welcome.

4

2 回答 2

38

Like so:

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))
}

And in action:

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
于 2009-09-01T20:01:56.463 回答
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")
  }
}
于 2021-04-21T00:02:43.360 回答