Does anyone know to translate the whenComplete
method of java CompletableFuture
to Scala? I really don't know how to do it and I'm kinda' stuck. Thank you
Here's an example that shows you how it might work (inside the 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
Create a future that waits for 5 seconds, then completes returning a string value. (See also the note below explaining how this works.)
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]
Now have some code execute when the future has finished. (This is where you should begin with your own whenComplete
implementation.)
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]
(Note that, in this case, the future completed before I was able to type in the whenComplete
method. This is clearly a race condition, so if you paste the whole lot into the REPL at once, you may see that res0
is determined to have "Not completed", and then see the output of the whenComplete
function.)
So what's going on here, because this code doesn't look much like the JavaDoc for the associated classes?
It's a little Scala magic called single abstract methods . Essentially, if an asbtract class (or trait) has a single abstract method, then you can replace an instance of that class with the definition of the abstract method. Further, Scala knows which class is relevant from the argument list of the associated function.
Let's start with CompletableFuture.supplyAsync
, which takes a single Supplier[T]
argument. In Scala terms, this type looks like this:
trait Supplier[T] {
def get(): T
}
So we could have written the creation of the future
element as follows instead:
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]
Because the Scala compiler knows that supplyAsync
takes a Supplier[T]
, and because Supplier[T]
has a single abstract method , get
, the compiler is able to accept the abbreviated form that uses a function literal as the definition of both Supplier
and its get
method.
We then use the same approach with the whenComplete
method. Here, the type of argument is a BiConsumer[T, U]
(where T
is the type of value returned by the future, and U
is an exception type), which takes a single abstract method accept
. (This type also has an andThen
method, but that's not abstract, so it doesn't matter to Scala .) So, to have been more explicit, we could have written the following:
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]
Both approaches are valid, so feel free to use whichever one makes sense to you...
Note that whenComplete
is a lot uglier than conventional Scala code typically requires: If the future threw an exception instead of successfully finishing, then error
will be non- null
; otherwise, result
will contain the result of the future, which could also be null
.
If at all possible, I would strongly recommend using Scala Future
s instead. Far more functional, and far more elegant than those in Java .
You can convert a Java CompletableFuture
into a Scala Future
with the following implicit conversion:
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
}
You can then handle completion of the Java future far more elegantly (again, in the REPL, with the above defined):
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'...")
| }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.