2

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 Answer 1

4

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)
Sign up to request clarification or add additional context in comments.

3 Comments

+1, that's correct, SBT Build.scala is providing CL on calling end, and then callee application class has it's own CL. I can do all the work in application class but would be more appropriate to do in Build.scala for this particular task. I don't understand what you mean by calling getCL on returned Map and on a freshly created one.
@virtualeyes - The maps from two distinct class loaders are not compatible with each other, even if the class is "the same" (exact same name and bytecode). The Map$Map2 thing is a red herring--even if the types were identical you would not be able to cast! See e.g. stackoverflow.com/questions/1735714
While not what I wanted to hear, this is a great answer in that I: 1) learned something new; 2) know not to waste anymore time and simply do the work on the callee end of things. No more head banging, thanks ;-)

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.