Scala - Динамическая загрузка объектов / классов - PullRequest
19 голосов
/ 15 января 2012

В Java я загружаю внешний класс (в файле .jar) следующим образом:

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

И он отлично работает.

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

Теперь я хочу сделать то же самое в Scala.У меня есть trait с именем Module (Module.scala):

trait Module {
  def name: String
}

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

Я пишу модуль, расширяющий Module, затем компилирую его в module.jar:

package my.module

import Module

object ExModule extends Module {}

Затем я загружаю его по этому коду:

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

Работает нормально.Но если я создаю новый экземпляр, я получаю это исключение:

java.lang.InstantiationException: my.module.ExModule

Если я проверяю его:

clazz.isInstanceOf[Module]

-> всегда возвращаю false.

ТакНе могли бы вы помочь мне в этой проблеме?

Отредактировано

Я думаю, это потому, что ExModule является object (не class).Но когда я изменяю его на class, а classLoader.loadClass(...) поднимает java.lang.NoClassDefFoundError.Я думаю, это потому, что ExModule расширен от trait.

Я в замешательстве.Может ли кто-нибудь помочь мне?

Отредактировано

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

возвращает true.

Ответы [ 2 ]

11 голосов
/ 15 января 2012

Упс ... Я получил ответ.

Учитесь у:

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

Я полагаю, что этот путь временный, прежде чем команда Scala предоставит правильный способ загрузки object/class/trait ... из внешнего файла JAR.Или потому что я не могу найти правильный путь.Но в настоящее время это помогает мне выйти из проблемы.

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

Вот и все: -)

Отредактировано

Я бы лучше проектировал ExModule как класс.После загрузки из файла JAR, я могу проверить это так:

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

Примечание:

Вы не можете сделать этообратный путь:

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

, поскольку Module является trait / object, isAssignableFrom() в этом случае не будет работать.

0 голосов
...