Scala, doesn't like java collections API?

covariance, java, scala, scala-java-interop

Solution

Well, the first error message made it clear— since `java.util.Map` is mutable, it can't handle variance. So you cannot cast a `Map[_,Set[_]]` to a `Map[_,Collection[_]]`.

val m: java.util.Map[String, java.util.Collection[String]] = 
   new java.util.HashMap[String,java.util.Collection[String]]()
val s = new java.util.HashSet[String]()
s.add("welt")
m.put("hallo", s)

Problem

I'm using asynchttpclient. When building up parameters, one passes a java.util.Map to the setParameters method. The (Java) signature looks like the following: ``` setParameters(Map<String,Collection<String>); ``` Real day 1 stuff. But wow, I'm trying to call it from Scala and I cannot for the life of me create a collection to match that signature. Here's the mess I've created so far. ``` var m:java.util.Map[java.lang.String,java.util.Collection[java.lang.String]] = new java.util.HashMap[java.lang.String,java.util.HashSet[java.lang.String]]() val req = new RequestBuilder().setUrl("http://localhost:1234/").setParameters(m).build ``` And here's the error message, ``` Multiple markers at this line - type mismatch; found : java.util.HashMap[java.lang.String,java.util.HashSet[java.lang.String]] required: java.util.Map[java.lang.String,java.util.Collection[java.lang.String]] Note: java.util.HashSet[java.lang.String] <: java.util.Collection[java.lang.String], but Java-defined **trait Map is invariant in type V. You may wish to investigate a wildcard type such as `_ <: java.util.Collection[java.lang.String]`. (SLS 3.2.10)** - type mismatch; found : ``` Fair enough, I wouldn't have thought I was doing something particularly complicated, but lets give the compiler suggestion a try... So I change it to the following ``` var m:java.util.Map[java.lang.String,_ <: java.util.Collection[java.lang.String]] = new java.util.HashMap[java.lang.String,java.util.HashSet[java.lang.String]]() val req = new RequestBuilder().setUrl("http://localhost:1234/").setParameters(m).build ``` And receive the following lovely error message in response. ``` Multiple markers at this line - overloaded method value setParameters with alternatives: (com.ning.http.client.FluentStringsMap)com.ning.http.client.RequestBuilder <and> (java.util.Map[java.lang.String,java.util.Collection[java.lang.String]])com.ning.http.client.RequestBuilder cannot be applied to (java.util.Map[java.lang.String,_$1]) ``` Back to basic's, I'd also like to mention that my first attempt was as follows. ``` import scala.collection.JavaConverters._ var m = Map[String,Set[String]]() val req = new RequestBuilder().setUrl("http://localhost:1234/").setParameters(m.asJava).build ``` But that produced the following Multiple markers at this line - overloaded method value setParameters with alternatives: (com.ning.http.client.FluentStringsMap)com.ning.http.client.RequestBuilder (java.util.Map[java.lang.String,java.util.Collection[java.lang.String]])com.ning.http.client.RequestBuilder cannot be applied to (java.util.Map[String,Set[String]]) Edit, thanks to __0, this is now working. Here's my final code: ``` def buildReqMap(in: Map[String, String]) = { import java.util.{ Map => JMap, Collection => JColl, HashMap => JHashM, HashSet => JHashS } val m: JMap[String, JColl[String]] = new JHashM[String, JColl[String]]() in.fold(m) { (a, b) => { val s = new JHashS[String]() s.add(b.asInstanceOf[String]) m.put(a.asInstanceOf[String], s) m } } m } def main(args: Array[String]): Unit = { val m = buildReqMap( Map( ("delimited" -> "length"), ("track" -> "binarytemple,music,kittens"))) val req = new RequestBuilder().setUrl("http://localhost:1234/").setParameters(m).build val fut = asyncHttpClient.executeRequest(req, handler).get ```

Original source