Вот пример, который показывает вам, как это может работать (внутри 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
.это явно условие гонки, поэтому, если вы вставите весь лот в REPL сразу, вы можете увидеть, что для res0
определено значение «Не выполнено», а затем увидеть результат выполнения функции whenComplete
.)
Так что здесь происходит, потому что этот код не очень похож на JavaDoc для связанных классов?
Это немного Scala magic называется отдельные абстрактные методы .По сути, если у класса (или свойства) asbtract есть один абстрактный метод, вы можете заменить экземпляр этого класса определением абстрактного метода.Далее, 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
будет не null
;в противном случае result
будет содержать результат будущего, который также может быть null
.
Если это вообще возможно, я настоятельно рекомендую использовать Scala Future
s.Гораздо более функциональный и намного более элегантный, чем в Java .
Вы можете преобразовать Java CompletableFuture
в Scala Future
со следующим неявным преобразованием:
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'...")
| }