无法在Scala中扩展Future [T]特征

时间:2013-11-01 16:47:08

标签: scala playframework-2.0

请原谅我对Scala的不理解。我只是一个想要在Play Framework中工作的Java开发人员。我甚至尝试使用Java代码实现一个特性,但是我得到了更加模糊的错误。我有以下Scala代码:

package models

import scala.concurrent.Future

class SettableFuture[T](name: String) extends Future[T] {
    var assignedValue: T

    def set(newValue: T) =
        assignedValue = newValue

    //override abstract methods in Future[T]
    def ready(atMost: scala.concurrent.duration.Duration)(implicit permit: scala.concurrent.CanAwait): models.SettableFuture[T] =
        this

    def result(atMost: scala.concurrent.duration.Duration)(implicit permit: scala.concurrent.CanAwait): T =
        assignedValue

    def isCompleted: Boolean =
        false

    def onComplete[U](func: scala.util.Try[T] => U)(implicit executor: scala.concurrent.ExecutionContext): Unit =
        null

    def value: Option[scala.util.Try[T]] =
        null
}

这是我的错误:

overriding method ready in trait Awaitable of type (atMost: scala.concurrent.duration.Duration)(implicit permit: scala.concurrent.CanAwait)SettableFuture.this.type; method ready has incompatible type

暂时忽略方法的返回值,它们是荒谬的,因为我只是想修复所有的编译错误。

我只是在扩展特征时从编译时异常中复制方法存根,而不覆盖其抽象方法并将它们粘贴到我的源文件中。我不明白为什么我仍然会遇到错误。我在Awaitable中查看了ready()的签名,看起来返回类型实际上应该是类。

编辑:我想实现这个的原因是因为在Promise / Future Scala API中,我只能找到让我异步执行长时间运行的阻塞任务的东西。我所追求的是让请求的执行暂停,直到感兴趣的东西在SettableFuture实例中设置一个值,完成Promise以发送响应。通过这种方式,它有点像延续。无论如何,这是我最终得到的工作代码:

package models

import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger

import scala.concurrent.CanAwait
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import scala.util.Try

class SettableFuture[T]() extends Future[T] {
    private final val ValueNotSet = 0
    private final val ValueBeingSet = 1
    private final val ValueSet = 2

    private val valueStatus: AtomicInteger = new AtomicInteger(ValueNotSet)
    private val onCompleteWaitHandle: CountDownLatch = new CountDownLatch(1)
    private var onComplete: Try[T] => _ = _
    private var assignedValue: T = _

    /** Set a value and complete this Future.
      *
      * Returns false if the value has already been set by a past call to this method.
      * Otherwise, marks this Future as complete, executes the function passed to
      * onComplete, and finally returns true.
      */
    def set(newValue: T): Boolean = {
        //set value and trigger onComplete only once
        if (valueStatus.compareAndSet(ValueNotSet, ValueBeingSet)) {
            assignedValue = newValue
            valueStatus.set(ValueSet)
            onCompleteWaitHandle.countDown()
            if (onComplete != null)
                onComplete(Try(assignedValue))
            true
        }
        false
    }

    //override abstract methods in the Future[T] trait
    def ready(atMost: Duration)(implicit permit: CanAwait): this.type = {
        onCompleteWaitHandle.await(atMost.length, atMost.unit)
        this
    }

    def result(atMost: Duration)(implicit permit: CanAwait): T = {
        ready(atMost)
        assignedValue
    }

    def isCompleted: Boolean = (valueStatus.get() == ValueSet)

    def onComplete[U](func: Try[T] => U)(implicit executor: ExecutionContext): Unit =
        onComplete = func

    def value: Option[Try[T]] = {
        if (!isCompleted)
            None
        Some(Try(assignedValue))
    }
}

3 个答案:

答案 0 :(得分:5)

就您所获得的具体错误而言,您覆盖的ready方法的返回类型为Awaitable.this.type - 即。此Awaitable实例的特定类型(Future的超类型,因此在Future中,此方法的返回类型为Future.this.type}。对于SettableFuture类,这意味着ready方法的返回类型必须为models.SettableFuture.this.type

您可能遇到的其他小问题:onComplete方法的实现应该是{},而不是null,因为后者是类型Null.type的返回,而不是Unit,var assignedValue需要在非抽象类中初始化,这可以通过将= _添加到定义变量的行来完成(尽管你真的想要它至少protected,并提供一个访问器,用于检查是否已设置 - 可能是将变量更改为Option[T]初始化为None,或者保留Boolean可以在访问者中检查的标记,以及true方法设置为set的标记。

然而,就您想要实现的目标而言,您可能只想查看scala.concurrent.Promise,这代表了“未来结果的承诺”。它有一个方法future,它返回Future,以及各种completecompleteWith和类似的方法,可用于设置Promise的值,这将导致相关的Future准备就绪/完成。

答案 1 :(得分:2)

SettableFuture课程不必要地混淆了FuturePromise特征被设计为分开的两个问题:

  • 异步提供值(Promise)和
  • 等待并对该条款做出反应(Future

不是将Future视为异步,长时间运行的阻塞计算,而是将其简单地视为可能在将来某个时间提供的值。您可以通过多种方式对该值的提供做出反应,包括注册回调或将其映射到其他值。例如,在Play中,通常会暂停使用这样的模式处理请求(在Scala中):

def handleRequest = Action {
  Async {
    gimmeAFuture().map(value => Ok(value))
  }
}

def gimmeAFuture(): Future[JsValue] = // ...

gimmeAFuture方法返回Future,但请求处理代码不关心如何计算值。它可能是

  • 使用Future.successful
  • 立即计算
  • Future.apply
  • 异步计算
  • 由完成Promise
  • 提供

作为后者的一个例子,gimmeAFuture方法可以实现如下:

def gimmeAFuture: Future[JsValue] = {
  val p = Promise.apply[JsValue]()
  // asynchronously complete the promise 30 seconds from now
  scheduler.scheduleOnce(30.seconds)(p.complete(Success(someJsObject)))
  // return the future immediately
  p.future
}

当然,您可以根据需要实施该方法。关键是需要保留某个Promise对象并使用值完成它以便继续处理请求。请求处理程序本身获取对Promise对象的引用,因为它只关心将要计算的值(即Promise的未来)。

答案 2 :(得分:0)

我在找到link之后重新审视了这个问题,我意识到为什么会有这么多混乱。

我打算使用SettableFuture类,因为我找不到Play Java API中已有的类似内容。我想要一些与.NET中的TaskCompletionSource相当的东西,而Aaron的回答清楚地表明Scala正是我所需要的。不幸的是,我在Play的Java API中找不到相同的东西。

这个链接清楚地解释了为什么我对那些应该如此简单的事情感到非常困难。谢谢大家回答我的问题!