4

I'm currently dealing with the AWS Java SDK, which expects Java types.

I've been using JavaConverters to explicitly converts types like maps and lists to java. I've been running into more and more complex types and found myself having to map deep into lists which is becoming unwieldy.

My current issue is I have a Map[String, List[String]] converting the overall object to java is simple but I'm being hit by type erasure when pattern matching the list in the map.

val t = m.map{
    case (k, v: List[String]) => k -> v //type erasure so unreachable
}

I found a great explanation of Type Tags in Scala which is an elegant solution I'd like to implement. Scala: What is a TypeTag and how do I use it?

I'm just unsure how I'm implement this into my pattern match.

I've implemented a method that looks like so

def isNestedMap[A : TypeTag](xs: A): Boolean = typeOf[A] match {
    case t if t =:= typeOf[Map[String, List[String]]] => true
    case t => false
}

And a new case case (k, v) if isNestedMap(v) => k -> "beans" but this always return false as my method is seeing types as an Any.

Any ideas how to surmount this? Or better yet a way of recursively convert deep lists or maps to their java equivalents?

Thanks all.

EDIT: The Map is actually a type of Map[String, Any] so I need to know the type of Any before I can convert it to Java

3 Answers 3

2

I don't think you problem is really related to erasure or even needs to make use of type tags. Here is quick session on the REPL demonstrating one way to make the conversions:

scala> import collection.JavaConverters._
import collection.JavaConverters._

scala> List(1,2,3).asJava
res0: java.util.List[Int] = [1, 2, 3]

scala> Map(1 -> List(1),2 -> List(2))
res1: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(1), 2 -> List(2))

scala> res1.asJava
res2: java.util.Map[Int,List[Int]] = {1=List(1), 2=List(2)}

scala> res1.map{ case (l,r) => l -> r.asJava}.asJava
res3: java.util.Map[Int,java.util.List[Int]] = {1=[1], 2=[2]}

scala> res1.mapValues(_.asJava).asJava
res5: java.util.Map[Int,java.util.List[Int]] = {1=[1], 2=[2]}
Sign up to request clarification or add additional context in comments.

2 Comments

I probably wasn't clear enough in my original answer but the Map type is actually Map[String, Any] so .asJava isn't available when mapping :(
So type tag can't possibly help you there.
1

EDIT: The Map is actually a type of Map[String, Any] so I need to know the type of Any before I can convert it to Java

TypeTags are inserted by the compiler according to the static type, so when you call isNestedMap(v), it'll see v: Any and insert isNestedMap[Any](v)(typeTag[Any]).

Or better yet a way of recursively convert deep lists or maps to their java equivalents?

Something like

def deepAsJava(x: Any): Any = x match {
  case l: List[_] => l.map(deepAsJava).asJava
  case m: Map[_, _] => m.map { case (k, v) => (k, deepAsJava(v)) }.asJava
  case ... // other collections you want to support
  case x => x
}

You can split it into separate functions for listDeepAsJava, etc. to get better type signatures for each:

def deepAsJava(x: Any): Any = x match {
  case l: List[_] => listDeepAsJava(l)
  case m: Map[_, _] => mapDeepAsJava(m)
  case ... // other collections you want to support
  case x => x
}

def listDeepAsJava(x: List[Any]): java.util.List[Any] = l.map(deepAsJava).asJava
...

3 Comments

This is almost exactly the solution I'm looking for. The only issue is that the result is a type of Any and I'm having to manually cast this using .asInstanceOf[java.util.List[java.util.Map[String, Any]]] any ideas how I could perhaps do this implicitly? It feels like splitting the methods up like you mentioned, would allow this but when attempting this it complains about type mismatches when the method's signature is listDeepAsJava(x: List[Any]): java.util.List[Any]
Yes, that's why I suggested splitting up methods. Note that you still need to use deepAsJava inside: I've updated the answer to clarify. Of course, it still gets you only java.util.List[Any]. To get java.util.List[java.util.Map[...]] is going to be a lot more complicated.
Thanks so much for all your help. This was the eloquent solution I was looking for!
0

Try with Array instead of List.

val m = Map[String, Array[String]]()
val t = m.map{
    case (k, v: Array[String]) => k -> v //no type erasure
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.