Scala 错误值flatMap不是可在for with Future[选项]中序列化的产品的成员

Scala 错误值flatMap不是可在for with Future[选项]中序列化的产品的成员,scala,Scala,以下代码块生成失败,出现错误: value flatMap is not a member of Product with Serializable [error] if (matchingUser.isDefined) { 代码如下: for { matchingUser <- userDao.findOneByEmail(email) user <- { if (matchingUser.isDefined) { matchingUs

以下代码块生成失败,出现错误:

value flatMap is not a member of Product with Serializable
[error]         if (matchingUser.isDefined) {
代码如下:

for {
  matchingUser <- userDao.findOneByEmail(email)
  user <- {
    if (matchingUser.isDefined) {
      matchingUser.map(u => {
        // update u with new values...
        userDao.save(u)
        u
      })
    } else {
      val newUser = new User(email)
      userDao.create(newUser)
      newUser
    }
  }
} yield user
用于{

matchingUser下面是我的测试示例,用解决方案重现您的问题。 基本上,您的
用户
返回关于Future的包装器(Future的选项),但它应该是Future,作为中的第一个语句,以供理解。 这就是为什么我应用了一些额外的展开。参见下面的示例

注意:它看起来不太好,我更喜欢用flatMap重写它

object T {

  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global

  def future1: Future[Option[Int]] = ???

  def future2(i: Int): Future[Double] = ???

  for {
    matchingUser <- future1
    user <- {
      if (matchingUser.isDefined) {
        matchingUser.map { i =>
          future2(i)
        }
      } else {
        Some(future2(42))
      }
    } match {
      case None => Future.successful(-42.0)
      case Some(x) => x
    }
  } yield user
}

if
语句的第一个分支返回
选项[User]
,另一个分支返回
User
。因此,整个语句的结果被推断为具有可序列化的类型
产品,因为它是这两个语句中唯一常见的超类型

您可以将
if
中的最后一条语句包装成
选项(只需执行
Option(newUser)
而不是
newUser
),或者更好的是,使用
fold
而不是整个
if(matchingUser.isDefined){…
thingy:

 matchingUser.fold { 
   val u = new User(email)
   userDao.create(u)
   u
 } { u => 
   userDao.save(u)
   u
 }
这将使该语句的结果成为您可能想要的
Option[User]
,但它仍然无法编译。 问题是你不能在理解中混合不同类型的单子:因为第一个单子是
Future
,其他单子也必须是。你不能在那里有
选项

如何解决这个问题?一种可能是让
userDao.create
userDao.save
返回他们刚刚保存的对象的未来。这可能是一种更好的做法,一般来说,比你拥有的更好,因为现在你在用户实际存储之前返回它…如果
create
操作n之后失败?然后你可以像这样重写你的理解:

for {
  matchingUser <- userDao.findOneByEmail(email)
  user <- matchingUser.fold(userDao.create(new User(email)))(userDao.save)
} yield user
或者,在这种情况下,使用模式匹配而不是
fold
看起来会更好一些:

 userDao
   .findOneByEmail(email)
   .flatMap { 
      case Some(u) => userDao.save(u)
      case None => userDao.create(new User(email))
    }

您也可以使用部分函数语法(
future1.flatMap{case Some(i)=>…
)而不是直接匹配。您在persist/update上是正确的。方法应该返回持久化/更新的记录,而不是Future[Unit]。你也说得对,如果我的代码像我发布的一样简单,那就太过分了。但实际上,我在发布之前对代码进行了大量简化,以确保我的问题直截了当,仅此而已。fold方法的另一个优点是,在你回答之前我并不知道。我会尽快接受你的答案谢谢!
  userDao
   .findOneByEmail(email)
   .flatMap(_.fold(usrDao.create(new User(email)))(userDao.save))
 userDao
   .findOneByEmail(email)
   .flatMap { 
      case Some(u) => userDao.save(u)
      case None => userDao.create(new User(email))
    }