user3750194
user3750194

Reputation: 407

Converting nested Scala type to Java types

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

Upvotes: 4

Views: 713

Answers (3)

Alexey Romanov
Alexey Romanov

Reputation: 170899

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
...

Upvotes: 1

Jus12
Jus12

Reputation: 18034

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
}

Upvotes: 0

pedrofurla
pedrofurla

Reputation: 12783

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]}

Upvotes: 2

Related Questions