2012-01-15 25 views
18

En Java, me carga la clase externa (en el archivo .jar) de esta manera:Scala - objeto dinámico/clase de carga

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

y funciona bien.

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

Ahora quieren hacer lo mismo en Scala. Tengo un trait llamado Module (Module.scala):

trait Module { 
    def name: String 
} 

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

que escribir un módulo que se extiende Module, a continuación, compilar a module.jar:

package my.module 

import Module 

object ExModule extends Module {} 

Entonces lo cargo por este código:

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

Funciona bien. Pero si creo nueva instancia, consigo esta excepción:

java.lang.InstantiationException: my.module.ExModule 

Si lo prueba:

-> siempre vuelven false.

¿Podría ayudarme con este problema?

Editado

supongo que se debe a que es un ExModuleobject (no class). Pero cuando lo cambio a class, y classLoader.loadClass(...) levanta un java.lang.NoClassDefFoundError. Supongo que es porque ExModule se extiende desde un trait.

Estoy confundido. ¿Podría alguien ayudarme por favor?

Editado

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

vuelve true.

+0

Creo que estabas más cerca de convertirlo en una clase (cuando recibías la ClassNotFoundException). ¿Estás seguro de que cuando hiciste referencia a module.jar estás obteniendo el camino correcto? El hecho de que se extienda desde un rasgo no debe hacer ninguna diferencia: ExModule es una clase (en ese caso). –

+0

Sí, estoy seguro de que la ruta a 'module.jar' es correcta. El método 'classLoader.loadClass (...)' funciona bien (en caso de que 'ExModule' sea' object'). –

+0

¿Puedes mirar el bytecode compilado (vía javap o similar)? Scalac tiene la extraña costumbre de agregar $ sy otros nombres extraños en todo el lugar, y tal vez su ExModule solo tiene un nombre un poco más en la versión compilada. Además, puede pedirle a Scalac que imprima el código java igual a su scala (simplemente no recuerdo la opción ahora). – Rogach

Respuesta

8

Vaya ... Tengo la respuesta.

Aprender de:

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

supongo que de esta manera es temporal antes que el equipo Scala proporciona manera correcta carga un object/class/trait ... desde el archivo jar externo. O porque no puedo encontrar el camino correcto. Pero actualmente esto me ayuda a salir del problema.

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

Eso es todo :-)

Editado

que será mejor diseño ExModule como clase. Después de cargarlo desde el archivo jar, puedo comprobar que como como como:

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

Nota:

No se puede hacer de la manera opuesta:

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

porque Module es un trait/object, isAssignableFrom() no funcionará en este caso.