20

In Java, I load external class (in .jar file) by this way:

ClassLoader classLoader = new URLClassLoader(new URL[] {
  new File("module.jar").toURI().toURL()});
Class clazz = classLoader.loadClass("my.class.name");
Object instance = clazz.newInstance();

//check and cast to an interface, then use it
if (instance instanceof MyInterface)
  ...

And it works fine.

====================

Now I want to do the same thing in Scala. I have a trait named Module (Module.scala):

trait Module {
  def name: String
}

object Module {
  lazy val ModuleClassName = "my.module.ExModule"
}

I write a module extending Module, then compile it to module.jar:

package my.module

import Module

object ExModule extends Module {}

Then I load it by this code:

var classLoader = new URLClassLoader(Array[URL](
  new File("module.jar").toURI.toURL))
var clazz = classLoader.loadClass(Module.ModuleClassName)

It works fine. But if I create new instance, I get this exception:

java.lang.InstantiationException: my.module.ExModule

If I test it:

clazz.isInstanceOf[Module]

-> always return false.

So could you help me on this problem?

Edited

I guess it is because ExModule is an object (not class). But when I change it to class, and classLoader.loadClass(...) raises a java.lang.NoClassDefFoundError. I guess it is because ExModule is extended from a trait.

I'm confused. Could anyone please help me?

Edited

clazz.isInstanceOf[Class[Module]]//or Class[Byte], or Class[_]...

returns true.

  • I think that you were closer with making it a class (when you were getting the ClassNotFoundException). Are you sure that when you referenced module.jar you are getting the path right? The fact that it is extended from a trait shouldn't make a any difference- ExModule is a class (in that case). – Chris Shain Jan 15 '12 at 06:16
  • Yes I'm sure the path to `module.jar` is correct. Method `classLoader.loadClass(...)` works fine (in case `ExModule` is `object`). –  Jan 15 '12 at 06:23
  • Can you look at the compiled bytecode (via javap or similar)? Scalac has funny habit of adding $s and other strange names all around the place, and maybe your ExModule just has a bit another name in the compiled version. Also, you can ask scalac to print the java code equal to your scala (I just don't remember the option now). – Rogach Jan 15 '12 at 10:11
  • @Rogach: It seems that is `my.module.ExModule$`. When I load class with that name, I got `java.lang.NoClassDefFoundError: scala/ScalaObject`. Then I add `/path/to/scala-library.jar` to `URLClassLoader()`, and got `java.lang.IllegalAccessException: Class main.scala.App$$anon$2$$anonfun$test$1 can not access a member of class my.module.ExModule$ with modifiers "private"` –  Jan 15 '12 at 10:44

2 Answers2

13

Oops... I got the answer.

Learn from:

====================

I guess this way is temporary before Scala team provides right way to load an object/class/trait... from external jar file. Or because I can't find out the right way. But currently this helps me out of the problem.

var classLoader = new java.net.URLClassLoader(
  Array(new File("module.jar").toURI.toURL),
  /*
   * need to specify parent, so we have all class instances
   * in current context
   */
  this.getClass.getClassLoader)

/*
 * please note that the suffix "$" is for Scala "object",
 * it's a trick
 */
var clazzExModule = classLoader.loadClass(Module.ModuleClassName + "$")

/*
 * currently, I don't know how to check if clazzExModule is instance of
 * Class[Module], because clazzExModule.isInstanceOf[Class[_]] always
 * returns true,
 * so I use try/catch
 */
try {
  //"MODULE$" is a trick, and I'm not sure about "get(null)"
  var module = clazzExModule.getField("MODULE$").get(null).asInstanceOf[Module]
} catch {
  case e: java.lang.ClassCastException =>
    printf(" - %s is not Module\n", clazzExModule)
}

That's all :-)

Edited

I'd better design ExModule as a class. After loading it from jar file, I can check it as like as:

var clazz = classLoader.loadClass(Module.ModuleClassName)
if (classOf[Module].isAssignableFrom(clazz))
  ...

Note:

You can not do it the opposite way:

if (clazz.isAssignableFrom(classOf[Module]))

because Module is a trait/object, isAssignableFrom() will not work in this case.

Community
  • 1
  • 1
0

Have you seen this article? http://kneissl.eu/Members/martin/blog/reflection-from-scala-heaven-and-hell

Got it from Need to load a scala class at runtime from a jar and initialize it

Also (and maybe more relevant): http://coffeetimecode.wordpress.com/2010/07/24/remote-and-dynamic-class-loading-in-scala/

Community
  • 1
  • 1
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • Thanks, I have read that question before posting this one. But I'm still confused :-(. I'm trying again... –  Jan 15 '12 at 07:23