Ошибка в методе, возвращающем Future[Option[BasicProfile]]

Я пишу приложение Play 2.3, используя SecureSocial (основная версия). Я создал LoginUser, который представляет данные пользователя в моей системе.

/**
 * Class representing a User in the system.
 * @constructor Create a new LoginUser instance from a BasicProfile object.
 * @param profile represents the BasicProfile object associated with the user.
 */
case class LoginUser(val profile: BasicProfile)

Теперь я пытаюсь реализовать черту UserService[T] (из SecureSocial), но у меня проблемы.

/**
 * Class tha handle all the request for read/write user data in/from the database.
 */
class InMemoryUserService extends UserService[LoginUser] {

  private var tokens = Map[String, MailToken]()
  /**
   * Finds a user by provider id and user id.
   * @param providerId - the user provider id.
   * @param userId - the user user id.
   * @return an optional user
   */
  def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
    val future: Future[Option[LoginUser]] = UserServiceLogin.find(Json.obj("providerId" -> providerId, "userId" -> userId)).one
    future onComplete {
      case Success(Some(x)) => return x.profile
      case _ => None
    }
}

Метод find возвращает объект Future[Option[BasicProfile]], но компилятор говорит мне, что код неверен. Вот вывод компилятора:

[error] /Users/alberto/git/recommendation-system/app/security/UserService.scala:68: type mismatch;
[error]  found   : securesocial.core.BasicProfile
[error]  required: scala.concurrent.Future[Option[securesocial.core.BasicProfile]]
[error]       case Success(Some(x)) => return x.profile

Что случилось?? Как мне решить мою проблему??


person alberto adami    schedule 22.09.2014    source источник


Ответы (1)


Вы не должны регистрировать обратный вызов на своем Future через onComplete. Вместо этого вы хотите map изменить тип содержимого:

def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
    val future: Future[Option[LoginUser]] = UserServiceLogin.find(Json.obj("providerId" -> providerId, "userId" -> userId)).one

    future map {
      case Some(x) => Some(x.profile)
      case None => None
    }
}

Это должно сделать компиляцию счастливой :) Обратите внимание, что возвращаемый тип onCompleteUnit, а с помощью map я могу применить любые изменения к содержимому Future.

Менее подробная версия, которая map содержит как Future, так и Option:

def find(providerId: String, userId: String): Future[Option[BasicProfile]] = {
    val future: Future[Option[LoginUser]] = UserServiceLogin.find(Json.obj("providerId" -> providerId, "userId" -> userId)).one

    future.map(_.map(_.profile)
}
person vptheron    schedule 22.09.2014
comment
Спасибо. Но первый из кодов, которые вы опубликовали, не компилируется для меня. Здесь ошибки компилятора: [ошибка] /Users/alberto/git/recommendation-system/app/security/UserService.scala:87: конструктор не может быть создан для ожидаемого типа; [ошибка] найдено: Some[A] [ошибка] требуется: scala.concurrent.Future[Option[security.LoginUser]] [ошибка] case Some(x) => Some(x.profile) - person alberto adami; 22.09.2014
comment
@albertoadami ха, извините, забыл позвонить map :) Исправлено. Я думаю, вам следует предпочесть вторую форму, более простую и идиоматическую. Я думаю, что case Some(x) => Some(x.profile) довольно уродлив, я просто опубликовал его, чтобы подчеркнуть, что единственная реальная разница заключается в вызове map вместо onComplete. - person vptheron; 22.09.2014