I originally wrote this tutorial about Scala’s JavaConversions
object. That original text is still available down below (below the horizontal rule), but as a quick update, the JavaConversions object has been deprecated in favor of the JavaConverters object, which I’ll demonstrate here.
2019 Update: The correct approach is to now use the scala.jdk.CollectionConverters object to convert from Java collections classes to Scala collections classes. Also, use the scala.jdk.javaapi.CollectionConverters object to convert from Scala collections classes to Java collections classes. While those classes/objects have changed, the general approach is similar to what’s shown below.
A JavaConverters example
Here’s a brief example of how to use the JavaConverters object to convert a Java List
to a Scala Buffer
:
scala> import scala.collection.JavaConverters._ import scala.collection.JavaConverters._ scala> val jList = java.util.Arrays.asList(1, 2, 3) jList: java.util.List[Int] = [1, 2, 3] scala> val sBuffer = asScalaBuffer(jList) sBuffer: scala.collection.mutable.Buffer[Int] = Buffer(1, 2, 3)
As shown, the keys to the solution are:
- To import the JavaConverters object into scope.
- To know which conversion you should use. In the case of a Java
List
, the JavaConverters object lets you convert it to a ScalaBuffer
.
Converting from a Buffer to other Scala sequences
Note that once you have the Java List
as a Scala Buffer
, you can convert that Buffer
to any other Scala data type you want, such as Seq
, Vector
, etc., as shown in the REPL:
scala> sBuffer.to<tab> to toBuffer toIterable toList toParArray toSet toString toVector toArray toIndexedSeq toIterator toMap toSeq toStream toTraversable
Converting a Java Set to a Scala Set
Similar converters are available for other Java and Scala data types. For example, this is how you convert a Java Set
to a Scala Set
:
scala> val jSet = new java.util.HashSet[Int]() jSet: java.util.HashSet[Int] = [] scala> jSet.addAll(jList) res0: Boolean = true scala> jSet res1: java.util.HashSet[Int] = [1, 2, 3] scala> val sSet = asScalaSet(jSet) sSet: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
That’s a brief look at JavaConverters. The rest of this tutorial is about the JavaConversions object, which, as mentioned, is now deprecated.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 17.1, “How to go to and from Java collections in Scala.”
Problem
You’re using Java classes in a Scala application, and those classes either return Java collections, or require Java collections in their method calls.
Solution
Use the methods of Scala’s JavaConversions
object to make the conversions work.
For instance, the java.util.ArrayList
class is commonly used in Java applications, and you can simulate receiving an ArrayList
from a method in the REPL, like this:
scala> def nums = { | var list = new java.util.ArrayList[Int]() | list.add(1) | list.add(2) | list | } nums: java.util.ArrayList[Int]
Even though this method is written in Scala, when it’s called, it acts just as though it was returning an ArrayList
from a Java method:
scala> val list = nums list: java.util.ArrayList[Int] = [1, 2]
However, because it’s a Java collection, I can’t call the foreach
method on it that I’ve come to know and love in Scala, because it isn’t there:
scala> list.foreach(println) <console>:10: error: value foreach is not a member of java.util.ArrayList[Int] list.foreach(println) ^
But by importing the methods from the JavaConversions
object, the ArrayList
magically acquires a foreach
method:
scala> import scala.collection.JavaConversions._ import scala.collection.JavaConversions._ scala> list.foreach(println) 1 2
This “magic” comes from the power of Scala’s implicit conversions, which are demonstrated in Recipe 1.10, “Add Your Own Methods to the String Class”.
Discussion
When you get a reference to a Java collections object, you can either use that object as a Java collection (such as using its Iterator
), or you can convert that collection to a Scala collection. Once you become comfortable with Scala collection methods like foreach
, map
, etc., you’ll definitely want to treat it as a Scala collection, and the way to do that is to use the methods of the JavaConversions
object.
As a more thorough example of how the JavaConversions
methods work, assume you have a Java class named JavaExamples
with the following getNumbers
method:
// java public static List<Integer> getNumbers() { List<Integer> numbers = new ArrayList<Integer>(); numbers.add(1); numbers.add(2); numbers.add(3); return numbers; }
You can attempt to call that method from Scala code, as shown in this example:
val numbers = JavaExamples.getNumbers() numbers.foreach(println) // this won't work
But this code will fail with the following compiler error:
value 'foreach' is not a member of java.util.List[Integer]
To solve this problem, you need to import the JavaConversions.asScalaBuffer
method. When you do this, you can either explicitly call the asScalaBuffer
method, or let it be used implicitly. The explicit call looks like this:
import scala.collection.JavaConversions.asScalaBuffer val numbers = asScalaBuffer(JavaExamples.getNumbers) numbers.foreach(println) // prints 'scala.collection.convert.Wrappers$JListWrapper' println(numbers.getClass)
The implicit use looks like this:
import scala.collection.JavaConversions.asScalaBuffer val numbers = JavaExamples.getNumbers numbers.foreach(println) // prints 'java.util.ArrayList' println(numbers.getClass)
The println(numbers.getClass)
calls show that there’s a slight difference in the result between the explicit and implicit uses. Using the explicit asScalaBuffer
method call makes the numbers
object an instance of collection.convert.Wrappers$JListWrapper
, whereas the implicit use shows that numbers
is an ArrayList
. As a practical matter, you can use either approach, depending on your preferences about working with implicit conversions; they both let you call foreach
, map
, and other Scala sequence methods.
You can repeat the same example using a Java Map
and HashMap
. First, create this method in a JavaExamples
class:
// java public static Map<String, String> getPeeps() { Map<String, String> peeps = new HashMap<String, String>(); peeps.put("captain", "Kirk"); peeps.put("doctor", "McCoy"); return peeps; }
Then, before calling this method from your Scala code, import the appropriate JavaConversions
method:
import scala.collection.JavaConversions.mapAsScalaMap
You can then call the mapAsScalaMap
method explicitly, or allow it to be called implicitly:
// explicit call val peeps1 = mapAsScalaMap(JavaExamples.getPeeps) // implicit conversion val peeps2 = JavaExamples.getPeeps
Again there is a difference between the types of the map objects. In this case, peeps1
, which used the explicit method call, has a type of collection.convert.Wrappers$JMapWrapper
, whereas peeps2
has a type of java.util.HashMap
.
Note that the JavaConversions
class has been through a number of changes, and although you’ll see a large number of conversion methods in your IDE, many of them are deprecated. See the latest Scaladoc for the JavaConversions
object for up-to-date information.
Conversion tables
One interesting thing that happens during the process of converting Java collections is that you learn more about the Scala collections. For instance, given their names, you might expect a Scala List
and a Java List
to convert back and forth between each other, but that isn’t the case. A Java List
is much more like a Scala Seq
or a mutable Buffer
.
This is shown in Table 17-1, which shows the two-way conversions that the JavaConversions
object allows between Java and Scala collections. This table is adapted from the JavaConversions
documentation.
Table 17-1. The two-way conversions provided by the JavaConversions
object
Scala collection | Java collection |
---|---|
collection.Iterable | java.lang.Iterable |
collection.Iterable | java.util.Collection |
collection.Iterator | java.util.{Iterator, Enumeration} |
collection.mutable.Buffer | java.util.List |
collection.mutable.Set | java.util.Set |
collection.mutable.Map | java.util.{Map, Dictionary} |
collection.mutable.ConcurrentMap | java.util.concurrent.ConcurrentMap |
As an example of the two-way conversions shown in Table 17-1, the JavaConversions
object provides methods that convert between a Java List
and a Scala Buffer
. The asScalaBuffer
method converts a Java List
to a Scala Buffer
, and bufferAsJavaList
converts in the opposite direction, from a Buffer
to a List
.
Going from Scala collections to Java collections
So far you’ve looked primarily at converting Java collections to Scala collections. You may also need to go in the other direction, from a Scala collection to a Java collection.
If you’re converting a Scala collection to a Java collection, in addition to the two-way conversions shown in Table 17-1, the one-way conversions shown in Table 17-2 are available. Again, these have been adapted from the JavaConversions
Scaladoc.
Table 17-2. The Scala to Java one-way conversions provided by the JavaConversions
class
Scala collection | Java collection |
---|---|
collection.Seq | java.util.List |
collection.mutable.Seq | java.util.List |
collection.Set | java.util.Set |
collection.Map | java.util.Map |
collection.mutable.Map[String,String] | java.util.Properties |
For example, assume you want to call the following sum method declared in a Java class named ConversionExamples
, which expects a java.util.List<Integer>
:
// java public static int sum(List<Integer> list) { int sum = 0; for (int i: list) { sum = sum + i; } return sum; }
Putting the conversion tables to work, the following examples show how to pass a Seq
, ArrayBuffer
, and ListBuffer
to the sum
method:
import scala.collection.JavaConversions._ import scala.collection.mutable._ val sum1 = ConversionExamples.sum(seqAsJavaList(Seq(1, 2, 3))) val sum2 = ConversionExamples.sum(bufferAsJavaList(ArrayBuffer(1,2,3))) val sum3 = ConversionExamples.sum(bufferAsJavaList(ListBuffer(1,2,3)))
There are many other collection conversion possibilities, and hopefully these examples will get you started on the right path.
The JavaConverters
object
The Scala JavaConverters
object lets you perform conversions in a manner similar to the examples shown, though they don’t offer implicit conversions. Instead they require you to explicitly call asJava
or asScala
methods to perform the conversions. Be careful, because the object also contains many deprecated methods.
See Also
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |