Подпись whenComplete от CompletableFuture

2

Кто-нибудь знает, чтобы перевести метод whenComplete java CompletableFuture на Scala? Я действительно не знаю, как это сделать, и я как бы застрял. Спасибо

  • 0
    Вы имеете в виду ту же семантику, но с использованием фьючерсов Scala? В таком случае недостаточно onComplete ?
  • 0
    Нет, я имею в виду, что я использую Java CompletableFuture внутри класса Scala, и мне нужна переведенная подпись whenComplete. Я не могу заставить его работать
Теги:
completable-future

1 ответ

2
Лучший ответ

Вот пример, который показывает вам, как это может работать (внутри scala REPL)...

$ scala
Welcome to Scala 2.12.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_181).
Type in expressions for evaluation. Or try :help.

scala> import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletableFuture

Создайте будущее, которое ждет 5 секунд, затем завершает возврат строкового значения. (См. Также примечание ниже, объясняющее, как это работает.)

scala> val future = CompletableFuture.supplyAsync {() =>
     |   Thread.sleep(5000) // Wait 5,000 ms (5 seconds).
     |   "We done did it!"
     | }
future: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture@5a466dd[Not completed]

Теперь, когда закончится будущее, выполняйте код. (Здесь вы должны начать со своей собственной реализации " whenComplete).

scala> future.whenComplete {(result, error) =>
     |   println(s"Result was: '$result', error was: '$error'")
     | }
Result was 'We done did it!'; error was 'null'
res0: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture@3ea9a091[Completed normally]

(Обратите внимание, что в этом случае будущее было завершено до того, как я смог whenComplete метод whenComplete. Это явно условие гонки, поэтому, если вы вставляете всю партию в REPL сразу, вы можете увидеть, что res0 определяется как имеют "Не завершен", а затем видят вывод функции whenComplete.)

Итак, что происходит здесь, потому что этот код не похож на JavaDoc для связанных классов?

Это небольшая магия Scala, называемая единым абстрактным методом. По сути, если класс asbtract (или trait) имеет единственный абстрактный метод, то вы можете заменить экземпляр этого класса на определение абстрактного метода. Кроме того, Scala знает, какой класс относится к списку аргументов связанной функции.

Начнем с CompletableFuture.supplyAsync, который принимает единственный аргумент Supplier[T]. В терминах Scala этот тип выглядит следующим образом:

trait Supplier[T] {
  def get(): T
}

Таким образом, мы могли бы написать создание future элемента следующим образом:

scala> import java.util.function.Supplier
import java.util.function.Supplier

scala> val future = CompletableFuture.supplyAsync {
     |   new Supplier[String] {
     |     override def get(): String = {
     |       Thread.sleep(5000) // Wait 5,000 ms (5 seconds).
     |       "We done did it!"
     |     }
     |   }
     | }
future: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture@35becbd4[Not completed]

Поскольку компилятор Scala знает, что supplyAsync берет Supplier[T], и поскольку у Supplier[T] есть один абстрактный метод, get, компилятор может принять сокращенную форму, которая использует литерал функции как определение как Supplier и его get метод.

Затем мы используем тот же подход с методом whenComplete. Здесь тип аргумента является BiConsumer[T, U] (где T представляет собой тип значения, возвращаемого в будущем, и U представляет собой тип исключения), который принимает один абстрактный метод accept. (Этот тип также имеет метод andThen, но не абстрактный, поэтому для Scala это не имеет значения.) Итак, чтобы быть более явным, мы могли бы написать следующее:

scala> import java.util.function.BiConsumer

scala> future.whenComplete {
     |   new BiConsumer[String, Throwable] {
     |     override def accept(result: String, error: Throwable): Unit = {
     |       println(s"Result was: '$result', error was: '$error'")
     |     }
     |   }
     | }
Result was 'We done did it!'; error was 'null'
res0: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture@3ea9a091[Completed normally]

Оба подхода действительны, поэтому не стесняйтесь использовать то, что имеет смысл для вас...

Обратите внимание, что whenComplete намного уродливее, чем обычно требуется для обычного кода Scala: если будущее создало исключение вместо успешной обработки, тогда error будет non- null; в противном случае result будет содержать результат будущего, который также может быть null.

Если это вообще возможно, я бы настоятельно рекомендовал вместо этого использовать Scala Future. Более функциональный и гораздо более элегантный, чем в Java.

Вы можете преобразовать Java CompletableFuture в Future Scala со следующим неявным преобразованием:

import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.language.implicitConversion
import scala.util.{Failure, Success}

implicit def completableFutureToFuture[T](cf: CompletableFuture[T]): Future[T] = {
  val p = Promise[T]() // Promise monitoring result of java cf.

  // Handle completion of Java future.
  cf.whenComplete {(result, error) =>

    // If error is not null, then future failed.
    if(error ne null) p.failure(error)

    // Otherwise, it succeeded with result.
    else p.success(result)
  }

  // Return the Scala future associated with the promise.
  p.future
}

Затем вы можете обрабатывать завершение Java-проекта гораздо более элегантно (опять же, в REPL, с указанным выше):

scala> val scalaFuture = future // Implicit conversion called.
scalaFuture: scala.concurrent.Future[String] = Future(Success(We done did it!))

scala> scalaF.onComplete {
     |   case Success(s) => println(s"Succeeded with '$s'")
     |   case Failure(e) => println(s"Failed with '$e'...")
     | }
  • 0
    Высоко ценится, спасибо :)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню