Mike Slinn

scala.jdk Converter Libraries Part 2

— Draft —

Published 2014-03-10. Last modified 2019-07-20.
Time to read: 6 minutes.

This lecture concludes the material begun in the previous lecture, and covers DurationConverters, FunctionConverters and FutureConverters.

DurationConverters

It is easy to convert between Scala and Java durations, and back again. To do this, of course, we must import the implicit conversions defined in the DurationConverters object.

Scala REPL
scala> scala> import scala.jdk.DurationConverters._
import scala.jdk.DurationConverters._
scala> scala> java.time.Duration.oftab of ofDays ofHours ofMillis ofMinutes ofNanos ofSeconds
scala> scala> val duration = java.time.Duration.ofSeconds(1) duration: java.time.Duration = PT1S
scala> scala> import scala.jdk.DurationConverters._ import scala.jdk.DurationConverters._
scala> scala> duration.toScala res7: scala.concurrent.duration.FiniteDuration = 1 second
scala> scala> duration.toScala.toJava res8: java.time.Duration = PT1S

Scala supports a nicer syntax for durations, if postfixOps is enabled and the implicit conversions defined in the concurrent.duration package are imported.

Scala REPL
scala> scala> import scala.language.postfixOps
import scala.language.postfixOps
scala> scala> import scala.concurrent.duration._ import scala.concurrent.duration._
scala> scala> val duration = 100 millis duration: scala.concurrent.duration.FiniteDuration = 100 milliseconds

We can convert any Java duration back to Scala.

Scala REPL
scala> scala> duration.toJava
res1: java.time.Duration = PT0.1S
scala> scala> duration.toJava.toScala res2: scala.concurrent.duration.FiniteDuration = 100000000 nanoseconds

Scala provides methods like toMillis that obtain the scalar quantity from a duration.

Scala REPL
scala> scala> duration.toJava.totab
toDays       toHours       toMillis       toMinutes       toNanos       toScala     toSecondsPart
toDaysPart   toHoursPart   toMillisPart   toMinutesPart   toNanosPart   toSeconds   toString
scala> scala> duration.toJava.toMillis res11: Long = 100

Java does not support infinite durations, so Scala's infinite duration cannot be converted to Java.

Scala REPL
scala> scala> Duration.Inf.toJava
                    ^
       error: value toJava is not a member of scala.concurrent.duration.Duration.Infinite

FunctionConverters

Scala/Java interop is a big topic, especially the aspects related to FunctionConverters. During the time of Java 6 and 7, Scala 2.9 and 2.10 we had an entire course on it. The topic became a lot more complex with the advent of Java 8, and when Scala 2.13 came out it subsumed the scala-java-interop package. Happily, the versions of Java up to Java 12 do not change the Java side of the equation much. Unfortunately, the official documentation pertaining to Scala/Java interop is currently out of date, the new features have not been properly documented, and most available code examples no longer work or are no longer recommended practice. This section cannot cover all that material, and so Scala/Java interop is out of scope for this course. Asking "why" about the material presented in this section is like pulling on a thread from a sweater ... the thread just keeps getting longer. Scala 3, due early in 2020, will change the interop API yet again, rendering this lecture out of date in just a few months. For those reasons, we won't answer questions on FunctionConverters except to fix errors. As you know, this is a Scala course, not a Java course, so Java is not explained here and we won't answer questions about Java. Please let us know if Scala-Java interop is of interest, and if there is sufficient demand we'll initiate a research project that results in updated, usable material in a new course dedicated to that topic. In the event that we republish a Scala-Java interop course, we would address and support both languages, and could provide information on interop from any other language that runs on the JVM.

The Scaladoc for FunctionConverters tells part of the story. I include it here, with a complete REPL transcript and some extra code and commentary.

This code creates a lambda expression called f. We discussed lambda functions in the More Fun With Functions and Lambda Review & Drill lectures  of the Introduction to Scala course. The lambda function is then converted to a java.util.function.IntUnaryOperator by calling the asJava method. This is not a Java course. If you need to get Java/Scala interoperating, you need to know Java and Scala. This course only teaches Scala.

Scala REPL
scala> scala> import scala.jdk.FunctionConverters._
import scala.jdk.FunctionConverters._
scala> scala> val f = (x: Int) => x + 1 f: Int => Int = $$Lambda$986/0x00000008405df040@6cf50207
scala> scala> val jf1 = f.asJava jf1: java.util.function.IntUnaryOperator = AsJavaIntUnaryOperator($$Lambda$986/0x00000008405df040@6cf50207)

A round trip yields the original function.

Scala REPL
scala> scala> val jf1 = f.asJava.asScala
jf1: Int => Int = $$Lambda$986/0x00000008405df040@6cf50207

More generic Java function types can be created using the corresponding asJavaXYZ extension method.

Scala REPL
scala> scala> val jf2 = f.asJavaFunction
jf2: java.util.function.Function[Int,Int] = AsJavaFunction($$Lambda$986/0x00000008405df040@6cf50207)

Again, round trips yield the original function.

Scala REPL
scala> scala> val jf2 = f.asJavaFunction.asScala
jf2: Int => Int = $$Lambda$986/0x00000008405df040@6cf50207
Scala REPL
scala> scala> val jf3 = f.asJavaUnaryOperator
jf3: java.util.function.UnaryOperator[Int] = AsJavaUnaryOperator($$Lambda$986/0x00000008405df040@6cf50207

Converting a Java function to Scala is done using the asScala extension method.

Scala REPL
scala> scala> List(1,2,3).map(jf2.asScala)
res1: List[Int] = List(2, 3, 4)

Now the problem I alluded to earlier: both of these imports define methods called asJava and asScala, but the definitions conflict.

Here is an example; this Scala method, which splits an incoming Java List[Int] into even and odd lists, is meant to be called from Java. The yellow portion creates a Scala tuple containing two Java List[Int]s by calling asJava. That asJava method is defined by importing CollectionConverters. The orange portion converts the Scala lambda function into a Java function.Function[JList[Int], (JList[Int], JList[Int])] - or it would, if the problem I am about to show you did not arise.

import java.util.{function, List => JList}
import scala.collection._
import scala.jdk.CollectionConverters._
import scala.jdk.FunctionConverters._
val intoEvenOdd: JList[Int] => (JList[Int], JList[Int]) = { javaList: JList[Int] => javaList.asScala.partition(_ % 2 == 0) match { case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) => (even.asJava, odd.asJava) } }.asJava

The error message is.

Error:(66, 72) type mismatch;
 found   : even.type (with underlying type scala.collection.mutable.Buffer[Int])
 required: ?{def asJava: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method BufferHasAsJava in trait AsJavaExtensions of type [A](b: scala.collection.mutable.Buffer[A])jdk.CollectionConverters.BufferHasAsJava[A]
 and method enrichAsJavaIntUnaryOperator in trait Priority0FunctionExtensions of type [A0](sf: A0 => Int)(implicit evA0: A0 =:= Int)scala.jdk.FunctionWrappers.RichFunction1AsIntUnaryOperator
 are possible conversion functions from even.type to ?{def asJava: ?}
        case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) => (even.asJava, odd.asJava)
Error:(66, 77) value asJava is not a member of scala.collection.mutable.Buffer[Int]
        case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) => (even.asJava, odd.asJava)
Error:(66, 85) type mismatch;
 found   : odd.type (with underlying type scala.collection.mutable.Buffer[Int])
 required: ?{def asJava: ?}
both method BufferHasAsJava in trait AsJavaExtensions of type [A](b: scala.collection.mutable.Buffer[A])jdk.CollectionConverters.BufferHasAsJava[A] and method enrichAsJavaIntUnaryOperator in trait Priority0FunctionExtensions of type [A0](sf: A0 => Int)(implicit evA0: A0 =:= Int)scala.jdk.FunctionWrappers.RichFunction1AsIntUnaryOperator are possible conversion functions from odd.type to ?{def asJava: ?} case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) => (even.asJava, odd.asJava) Error:(66, 89) value asJava is not a member of scala.collection.mutable.Buffer[Int] case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) => (even.asJava, odd.asJava) Error:(64, 84) type mismatch; found : java.util.List[Int] => <error> required: ?{def asJava: ?} Note that implicit conversions are not applicable because they are ambiguous: both method enrichAsJavaConsumer in trait Priority1FunctionExtensions of type [T](sf: T => Unit)scala.jdk.FunctionWrappers.RichFunction1AsConsumer[T] and method enrichAsJavaPredicate in trait Priority1FunctionExtensions of type [T](sf: T => Boolean)scala.jdk.FunctionWrappers.RichFunction1AsPredicate[T] are possible conversion functions from java.util.List[Int] => <error> to ?{def asJava: ?} val intoEvenOdd: JList[Int] => (JList[Int], JList[Int]) = { javaList: JList[Int] =>

The solution is to avoid having both imports in scope at the same time.

object FunctionConverterFun extends App {
  import java.util.{function, List => JList}
  import scala.collection._
val intoEvenOdd: JList[Int] => (JList[Int], JList[Int]) = { import scala.jdk.CollectionConverters._ javaList: JList[Int] => javaList.asScala.partition(_ % 2 == 0) match { case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) => (even.asJava, odd.asJava) } }
val intoEvenOddForJava: function.Function[JList[Int], (JList[Int], JList[Int])] = { import scala.jdk.FunctionConverters._ intoEvenOdd.asJava }
val jList: java.util.List[Int] = { import scala.jdk.CollectionConverters._ (1 to 10).asJava } println(intoEvenOddForJava(jList)) }

Calling Scala from Java

When calling Scala code from Java, Scala objects (which are implemented as singleton classes) must be accessed by appending a $ (dollar sign) to the name of the class, followed by the word MODULE$. Given a Scala object called FunctionConverterFromJava in package collection with a method intoEvenOddForJava, you would access it from Java by writing collection.FunctionConverterFromJava$.MODULE$.intoEvenOddForJava(). You would invoke the Java Function and pass in parameters with the apply method like this.

Shell
collection.FunctionConverterFromJava$.MODULE$.intoEvenOddForJava().apply(list1)

Breaking the above down.

  • The Scala package is called collection
  • The Scala object is called FunctionConverterFromJava, and it is accessed from Java as FunctionConverterFromJava$.MODULE$
  • The function is called intoEvenOddForJava, and it is dereferenced by adding parentheses after its name: intoEvenOddForJava()
  • The function is invoked by calling apply() and passing parameters to the function. Java 8 introduced the apply method for functions, and it was inspired by Scala's apply method. We discussed the apply method in the Objects lecture of the Introduction to Scala course.

Here is a portion of the Java program that invokes the above Scala code. Notice that Scala tuples can be used in Java, here we see a scala.Tuple2 in action..

Shell
import collections.FunctionConverterFromJava$;
import scala.Tuple2;
import java.util.Arrays;
import java.util.List;
public class FunctionConverterFun { public static void main(String[] args) { List<Object> list1 = Arrays.asList(1, 2); Tuple2<List<Object>, List<Object>> list2 = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava1().apply(list1); System.out.println("list2 = " + list2); } }

Notice that FunctionConverters as written requires the input collection to be declared as java.util.List<Object> instead of java.util.List<Integer>, and the returned collections must be declared as  java.util.List<Object>, which is annoying. We can return the desired type by declaring the types to be java.util.List<java.lang.Integer>. The Scala code for that looks like this.

import java.util.{function, List => JList}
import java.lang.{Integer => JInt}
val intoEvenOddForJava2: function.Function[JList[JInt], (JList[JInt], JList[JInt])] = { import scala.jdk.FunctionConverters._ intoEvenOdd2.asJava }

and the Java code which invokes the Scala code looks like this.

Shell
List<Integer> list3 = Arrays.asList(1, 2);
Tuple2<List<Integer>, List<Integer>> list4 = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava2().apply(list3);
System.out.println("list2 = " + list4);

This complete example, which includes more methods demonstrating interoperability between Java and Scala is provided in FunctionConverterFun.java and ConverterFun.scala.

Clearly this behavior is due to type erasure. Here is a StackOverflow answer that discusses a very similar issue. Explicitly declaring the types of the items in Scala collections to be Java primitive types makes the interop work. Since that approach works for scala.Int/java.lang.Integer, it is reasonable to assume similar issues and solutions for interoperability between Java and Scala for collections of scala.String/java.lang.String, collections of scala.Long/java.lang.Long, etc.

FutureConverters

This is not a course on Java, so if you are unfamiliar with Java Futures you should find another source of information to bring you up to speed before you attempt to make sense of the material presented here.

Quick Historical Recap

The Java Future interface was introduced in Java 5 to support asynchronous computation, but it did not have any methods for combining these computations or handling errors. The CompletableFuture class was introduced in Java 8. It implemented the Future interface and added the CompletionStage interface, which defines the contract for an asynchronous computation step that can be combined with other steps. CompletableFuture has about 50 different methods for composing, combining, executing asynchronous computation steps and error handling.

Akka 2.0 introduced its an implementation of Future during Scala 2.9's reign, then it was migrated and updated for Scala 2.10. I (Mike Slinn) published a book in 2012 entitled "Composable Futures with Akka 2.0 - Featuring Java, Scala and Akka Code Examples". Small improvements to Future have been made for each successive release of Scala.

For the Present State See the Scaladoc

The Scaladoc has a good overview of FutureConverters.

This object provides extension methods that convert between Scala Future and Java CompletionStage.

When writing Java code, use the explicit conversion methods defined in javaapi.FutureConverters instead.

Note that the bridge is implemented at the read-only side of asynchronous handles, namely Future (instead of scala.concurrent.Promise) and CompletionStage (instead of java.util.concurrent.CompletableFuture). This is intentional, as the semantics of bridging the write-handles would be prone to race conditions; if both ends (CompletableFuture and Promise) are completed independently at the same time, they may contain different values afterwards. For this reason, toCompletableFuture is not supported on the created CompletionStages.

Scala Future to Java CompletionStage

Before we can work with Scala Futures, we need an ExecutionContext in scope. This course introduced ExecutionContext in the  MultiThreading  lecture.

Scala REPL
scala> scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

Let's compute 1+2 in a Scala Future. Scala Futures was discussed extensively in this course over several lectures, starting with the Futures & Promises lecture.

Scala REPL
scala> scala> import scala.jdk.FutureConverters._
import scala.jdk.FutureConverters._
scala> scala> scala.concurrent.Future(1+2).asJava res1: java.util.concurrent.CompletionStage[Int] = scala.concurrent.impl.FutureConvertersImpl$CF@186a08b9[Not completed]

Look at all the methods that Java provides for CompletionStage.

Scala REPL
scala> scala> scala.concurrent.Future(1+2).asJava.tab
acceptEither         asScala         runAfterBoth          thenAccept            thenApply          thenCompose        toCompletableFuture
acceptEitherAsync    exceptionally   runAfterBothAsync     thenAcceptAsync       thenApplyAsync     thenComposeAsync   whenComplete
applyToEither        handle          runAfterEither        thenAcceptBoth        thenCombine        thenRun            whenCompleteAsync
applyToEitherAsync   handleAsync     runAfterEitherAsync   thenAcceptBothAsync   thenCombineAsync   thenRunAsync
scala> scala> scala.concurrent.Future(1+2).asJava.tabtab != acceptEitherAsync eq handleAsync runAfterBoth thenAcceptAsync thenCombineAsync toString ## applyToEither equals hashCode runAfterBothAsync thenAcceptBoth thenCompose wait + applyToEitherAsync exceptionally isInstanceOf runAfterEither thenAcceptBothAsync thenComposeAsync whenComplete -> asInstanceOf formatted ne runAfterEitherAsync thenApply thenRun whenCompleteAsync == asScala getClass notify synchronized thenApplyAsync thenRunAsync → acceptEither ensuring handle notifyAll thenAccept thenCombine toCompletableFuture

Java CompletableFuture to Scala Future

Before we use these converters, note that Java's CompletableFuture<T> is an implementation of two interfaces in the java.util.concurrent package: Future<T> and CompletionStage<T>. Thus a CompleteableFuture can be provided any place that a CompletionStage or a Java Future is required.

Here is some Java code that creates a Java CompletableFuture<T>. The Java CompletableFuture is converted to a Scala Future. I don't do anything with the Scala Future, which is not helpful, I admit. The real purpose of this code example is to set you up for the next code example. This code is provided in the git repository as FutureCallable.java.

import java.util.concurrent.*;
import scala.jdk.javaapi.FutureConverters;
public class FutureCallable { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(10);
CompletableFuture<String> future = new CompletableFuture<>(); scala.concurrent.Future<String> scalaFuture = FutureConverters.asScala(future);
System.out.println("Do something else while callable executes");
future.complete("All done!");
System.out.println("Retrieve the result of the future"); String result = future.get(); // Future.get() blocks until the result is available System.out.println(result);
executorService.shutdown(); } }

Here is a more useful example of how to work with Java Futures in Scala. This code is provided as FutureConverterFun.scala. Note how the Scala ExecutionContext is derived from the Java ExecutorService. Also note that the Scala andThen callback is used to pick up the result of the Java CompletableFuture. We discussed these techniques in the  Getting Results from Futures & Signaling with Promises lecture. You could also use Await.result to obtain the value.

Shell
package multi.futures
import java.util.concurrent.{CompletableFuture, ExecutorService, Executors} import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future} import scala.jdk.javaapi.FutureConverters import scala.util.{Failure, Success}
class FutureConverterFun { val executorService: ExecutorService = Executors.newFixedThreadPool(10) implicit val ec: ExecutionContextExecutor = ExecutionContext.fromExecutor(executorService)
val future = new CompletableFuture[String] val scalaFuture: Future[String] = FutureConverters.asScala(future)
scalaFuture.andThen { case Success(result) => println(s"Success: $result") case Failure(exception) => println(s"Failure: ${ exception.getMessage }") }.andThen {     case _ => System.exit(0)   } future.complete("All done!")   synchronized { wait() } }

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.