【问题标题】:Scala - Dynamic object/class loadingScala - 动态对象/类加载
【发布时间】:2012-01-19 17:47:42
【问题描述】:

在 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 中做同样的事情。我有一个名为ModuleModule.scala)的trait

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

那么你能帮我解决这个问题吗?

已编辑

我猜这是因为ExModuleobject(不是class)。但是当我将其更改为class 时,classLoader.loadClass(...) 会引发java.lang.NoClassDefFoundError。我猜这是因为ExModule 是从trait 扩展而来的。

我很困惑。谁能帮帮我?

已编辑

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

返回true

【问题讨论】:

  • 我认为你更接近于让它成为一个类(当你得到 ClassNotFoundException 时)。你确定当你引用 module.jar 时你得到了正确的路径吗?它从 trait 扩展而来的事实不应该有任何区别——ExModule 是一个类(在这种情况下)。
  • 是的,我确定module.jar 的路径是正确的。方法classLoader.loadClass(...) 工作正常(如果ExModuleobject)。
  • 你能看一下编译后的字节码(通过 javap 或类似的方式)吗? Scalac 有个有趣的习惯,就是到处添加 $s 和其他奇怪的名字,也许你的 ExModule 在编译后的版本中只是多了一个名字。此外,您可以要求 scalac 打印与您的 scala 相等的 java 代码(我现在不记得该选项了)。
  • @Rogach:好像是my.module.ExModule$。当我用那个名字加载类时,我得到了java.lang.NoClassDefFoundError: scala/ScalaObject。然后我将/path/to/scala-library.jar添加到URLClassLoader(),得到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"

标签: scala object dynamic load


【解决方案1】:

糟糕...我得到了答案。

学习:

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

我猜在 Scala 团队提供从外部 jar 文件加载 object/class/trait... 的正确方法之前,这种方式是暂时的。或者因为我找不到正确的方法。但是目前这可以帮助我解决问题。

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

因为Moduletrait/object,所以isAssignableFrom() 在这种情况下不起作用。

【讨论】:

    【解决方案2】:

    【讨论】:

    • 谢谢,我在发布这个问题之前已经阅读了这个问题。但我仍然很困惑:-(。我再试一次......
    猜你喜欢
    • 1970-01-01
    • 2013-04-12
    • 2012-02-10
    • 2013-12-14
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多