2
votes

Have a compile time task I need to run using reflection (bother caller and callee are Scala source files), but am getting a runtime error:

java.lang.ClassCastException: 
scala.collection.immutable.Map$Map2 cannot be cast 
to scala.collection.immutable.Map

Reflection class:

object jsRoutes {
  def getRoutesMap: Map[String,String] = {...}
}

Calling code: (loader is Scala 2.10.2 class loader)

val appLoader = new java.net.URLClassLoader(path, loader)
val clazz = appLoader.loadClass("controllers.jsRoutes")
val routesMap = clazz.getMethod("getRoutesMap")

Any attempt to cast java.lang.Object to expected return type Map[String,String] results in above ClassCastException

routesMap.invoke(new Object).asInstanceOf[Map[String,String]]...

or doesn't match:

routesMap.invoke(new Object) match {
  case x: Map[String,String] => ...
  case _ => println("not matched")
}

Have never heard of Map$Map2, in target class it's Map[String,String] so not sure what is converting the return type on reflection invocation.

Can println the uncasted java.lang.Object (Map) contents just fine.

Thanks for clues, this.is.frustrating ;-)

1

1 Answers

4
votes

You are probably trying to cast across class loaders. You can't do this--each class loader maintains its own hierarchy (for those classes not passed off to a common parent loader). Try calling getClassLoader on both your returned map and on a freshly-created one.

Incidentally, Map$Map2 is just an implementation detail--a subclass of Map for dealing with two-element maps. It casts just fine normally:

scala> val m = Map(1->"one", 2 -> "two"): Object
m: Object = Map(1 -> one, 2 -> two)

scala> m.getClass
res0: Class[_ <: Object] = class scala.collection.immutable.Map$Map2

scala> m.asInstanceOf[scala.collection.immutable.Map[Int,String]]
res1: scala.collection.immutable.Map[Int,String] = Map(1 -> one, 2 -> two)