Scala - Загрузка динамического объекта/класса
В 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
.
Так вы могли бы помочь мне в решении этой проблемы?
Edited
Я думаю, это потому, что ExModule
является object
(не class
). Но когда я меняю его на class
, а classLoader.loadClass(...)
вызывает a java.lang.NoClassDefFoundError
. Я думаю, это потому, что ExModule
расширяется от a trait
.
Я в замешательстве. Может ли кто-нибудь помочь мне?
Edited
clazz.isInstanceOf[Class[Module]]//or Class[Byte], or Class[_]...
возвращает true
.
Ответы
Ответ 1
К сожалению... Я получил ответ.
Учитесь у:
====================
Я предполагаю, что этот способ является временным до того, как команда 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 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)
}
Что все: -)
Edited
Мне лучше создать ExModule
как класс. После загрузки из файла jar я могу проверить его как:
var clazz = classLoader.loadClass(Module.ModuleClassName)
if (classOf[Module].isAssignableFrom(clazz))
...
Примечание:
Вы не можете сделать это обратным образом:
if (clazz.isAssignableFrom(classOf[Module]))
потому что Module
является trait
/object
, isAssignableFrom()
в этом случае работать не будет.
Ответ 2
Вы видели эту статью? http://kneissl.eu/Members/martin/blog/reflection-from-scala-heaven-and-hell
Получил его от Нужно загрузить класс scala во время выполнения из jar и инициализировать его
Также (и, возможно, более релевантный): http://coffeetimecode.wordpress.com/2010/07/24/remote-and-dynamic-class-loading-in-scala/