在Scala Futures地图和flatmap中实现null安全逻辑

时间:2013-11-24 16:59:49

标签: scala akka

我的代码包含从调用Akka actor返回的一些scala.concurrent.Futures

因此,演员中的示例代码如下所示:

val ivResult: Future[Any] = ask(ivActor, ivId)

// perform mapping is a function of type (Any) => Unit
val ivMapping: Future[Unit] = ivResult.map(performingMapping)

// erLookup is a function of type (Any) => Future[Any] 
val erResult: Future[Any] = ivResult.flatMap(erLookup)

等等。代码基本上由future.flatmap()。map()组成,用于执行聚合逻辑

我的问题是我想实现空安全逻辑,这样如果未来的结果为null,那么我们就不会抛出NullPointerExceptions

显然,我可以在每个函数中嵌入空值安全检查,但考虑到Scala的强大功能,这些看起来有点冗长。

因此,我想找出是否有更优雅的方法来做到这一点,也许是使用暗示等。

1 个答案:

答案 0 :(得分:1)

Future的结果已经表明它是否成功完成。

通常,在NPE上,您的代码会爆炸,未来会失败。

你说,请不要让我的代码爆炸,可能产生有害的副作用。

所以你想要一个有特殊失败状态的后卫。

类似的东西:

scala> object DeadFuture extends Exception with NoStackTrace
defined object DeadFuture

scala> implicit class SafeFuture[A](val f: Future[A]) {
     | def safeMap[B](m: A => B)(implicit x: ExecutionContext) =
     |   f.map { a: A => if (a == null) throw DeadFuture else m(a) }(x)
     | def safeFlatMap[B](m: A => Future[B])(implicit x: ExecutionContext) =
     |   f.flatMap { a: A => if (a == null) (Future failed DeadFuture) else m(a) }(x) 
     | }

然后

scala> case class Datum(i: Int)
defined class Datum

scala> def f(d: Datum): Int = 2 * d.i
f: (d: Datum)Int

scala> def g(d: Datum): Future[Int] = Future(2 * d.i)
g: (d: Datum)scala.concurrent.Future[Int]

scala> Future[Datum](null) safeMap f
res1: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@5fa5debb

scala> .value
res3: Option[scala.util.Try[Int]] = Some(Failure(DeadFuture$))

scala> def g(d: Datum): Future[Int] = Future(2 * d.i)
g: (d: Datum)scala.concurrent.Future[Int]

scala> Future[Datum](null) safeFlatMap g
res4: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise@7f188fd

scala> .value
res5: Option[scala.util.Try[Int]] = Some(Failure(DeadFuture$))

可能有更好的方法来促成转换,所以你要保持理解。

for (i <- safely(futureInt)) f(i)