0

请原谅我对 Scala 的理解不佳。我只是一个想要在 Play 框架中工作的 Java 开发人员。我什至尝试使用 Java 代码实现一个 trait,但我得到了更难以理解的错误。我有以下 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))
    }
}
4

3 回答 3

5

就您遇到的特定错误而言,ready您要覆盖的方法的返回类型为Awaitable.this.type- 即。此实例的特定类型Awaitable(的超类型Future,因此在 中Future,此方法被视为具有返回类型Future.this.type)。对于您的SettableFuture类,这意味着该方法的返回类型ready必须是models.SettableFuture.this.type.

您可能会遇到的其他小问题:onComplete方法的实现应该是{}, not null,因为后者是返回 type Null.type, not Unit,并且 varassignedValue需要在非抽象类中初始化,这可以通过= _添加定义变量的行(尽管您真的想至少创建它protected,并提供一个访问器来检查它是否已被设置 - 可能通过将变量更改为Option[T]初始化为None,或者维护一个Boolean可以在访问器中检查的标志,并且trueset方法设置为)。

然而,就您试图实现的目标而言,您可能只想检查一下scala.concurrent.Promise,它代表“对未来结果的承诺”。它有一个future返回 a的方法Future,以及各种complete,completeWith和类似的方法,可用于设置Promise's 的值,这反过来会导致关联Future的准备好/完成。

于 2013-11-01T17:07:18.713 回答
2

该类SettableFuture不必要地混合了FuturePromise特征旨在分离的两个问题:

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

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

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

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

gimmeAFuture 方法返回 a 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 的 Future)。

于 2013-11-02T00:03:42.077 回答
0

找到这个链接后,我重新审视了这个问题,我意识到为什么会有这么多的困惑。

我计划使用 SettableFuture 类,因为我找不到 Play Java API 中已经存在的任何类似的东西。我想要与 .NET 中的 TaskCompletionSource 等效的东西,而 Aaron 的回答清楚地表明 Scala 正是我所需要的。不幸的是,我在 Play 的 Java API 中找不到等价物。

该链接清楚地说明了为什么我在本应如此简单的事情上遇到了如此多的困难。谢谢大家回答我的问题!

于 2014-01-10T05:48:37.787 回答