Scala/Java integration problem: You need to share a Scala map with a Java method, or access a Java map in Scala code.
Update: The JavaConversions object in Scala has been deprecated. For a more modern approach, see my tutorial, How to convert a Java Map to a Scala Map using JavaConverters.
Solution
To use a Java map in Scala code, import the mapAsScalaMap
method from Scala’s JavaConversions
package, and perform the conversion.
Simply trying to use a Java HashMap
with something like a Scala for
loop won’t work. To demonstrate this, first create a simple Java HashMap
:
val jmap = new java.util.HashMap[String, String]() jmap.put("fname", "Alvin") jmap.put("lname", "Alexander")
Next, attempt to use the HashMap
in a Scala for
loop. You’ll see that it won’t work:
scala> for ((k,v) <- jmap) println(s"key: $k, value: $v") <console>:9: error: value filter is not a member of java.util.HashMap[String,String] for ((k,v) <- jmap) println(s"key: $k, value: $v") ^
However, by simply importing the correct method from the JavaConversions
package, the for
loop magically works:
scala> import scala.collection.JavaConversions.mapAsScalaMap import scala.collection.JavaConversions._ scala> for ((k,v) <- jmap) printf("key: %s, value: %s\n", k, v) key: lname, value: Alexander key: fname, value: Alvin
Discussion
As demonstrated in Recipe 17.1 in the Scala Cookbook, “Going to and from Java Collections,” the JavaConversions
package makes the process of going back and forth between Java collections very simple. The key to performing conversions is knowing which collections map to each other. There are lookup tables in that recipe that will help you make that choice.
Some developers don’t like the “magic” of implicit conversions, so if you prefer to be explicit about the conversion, call the conversion method from the JavaConversions
package directly (after importing it):
scala> val smap = mapAsScalaMap(jmap) smap: scala.collection.mutable.Map[String,String] = Map(lname -> Alexander, fname -> Alvin)
Java maps that don’t use generics
You can run into a problem when working with Java maps that don’t use generics. For instance, I ran into a problem when extending a Java interface as a Scala class. The BasicPlayerListener
is a Java interface whose opened method is declared like this:
// java void opened(java.lang.Object stream, java.util.Map properties);
The beginning of my Scala class definition looks like this:
class MyListener extends BasicPlayerListener { def opened(stream: Any, properties:Map[_, _] ) { // code here ...
Because I was implementing this interface, I needed to declare the Map
in my Scala opened method as follows to work with the java.util.Map reference in the opened
method of the Java interface:
def opened(stream: Any, properties:Map[_, _] ) { println("opened() called") }
Notice that it was necessary to define the map as Map[_, _]
. Fortunately Eclipse and the ScalaIDE project provided help when I was working on this override, because I first attempted to define the map as Map[Any, Any]
:
Note that java.util.Map does not match java.util.Map[Any,Any]. To implement a raw type, use java.util.Map[_, _].
In addition to defining the Map
with that syntax, I also needed these two import
statements in my Scala code to make everything work:
import scala.collection.JavaConversions._ import java.util.Map
See Also
- Recipe 17.1, “Going to and from Java Collections,” for details on the
JavaConversions
object - The
JavaConversions
object: http://www.scala-lang.org/api/current/index.html#scala.collection.JavaConversions$
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |