2

我在 Scala 中实现了一个自定义类加载器,用于将插件与主应用程序隔离开来。目前,我需要一个 Java 接口作为共享根对象,以便我的主应用程序可以使用插件代码。

充当共享根的接口(我希望这是 Scala):

public interface Handler {
    public List<HandlerInfo> getHandlers();
}

示例插件:

class MyPlugin extends Handler {
  def getHandlers: java.util.List[HandlerInfo] = // get some handlers
}

应用中的用法:

val jarFile     = new File(System.getProperty("user.dir") + "/plugins/" + jarName)
val cl          = new PluginLoader(jarFile, this.getClass.getClassLoader) // my custom classloader
val classToLoad = Class.forName(className, true, cl)
val handler     = classToLoad.newInstance.asInstanceOf[Handler]
val handlers    = handler.getHandlers

这很好用,但我的问题是我必须保留这个 Java 类(以及生成的构建配置)。我想改为使用 Scala 特征或抽象类,如下所示:

trait Handler {
  def getHandlers : List[HandlerInfo]
}

然后我的插件可能如下所示:

class MyPlugin extends Handler {
  def getHandlers: List[HandlerInfo] = // no more java.util.List
}

但我不能这样做,因为这条线

val handler     = classToLoad.newInstance.asInstanceOf[Handler]

抛出 a ClassCastException,大概是因为 Scala 编译器没有生成漂亮干净的 Java 接口。有什么办法可以解决这个问题,所以我可以拥有一个仅限 Scala 的项目吗?

4

1 回答 1

1

问题不在于您认为的问题所在;它在你的类加载器的某个地方。默认的类加载器工作得很好,即使是 Java 的。证据:

// File Handler.scala
trait Handler { def getHandlers: List[String] }

// File MyPlugin.scala
class MyPlugin extends Handler { def getHandlers = List("salmon", "cod") }

// File Interop.java
public class Interop {
  public static void main(String[] args) {
    try {
      Class classToLoad = Class.forName("MyPlugin");
      Handler handler = (Handler)classToLoad.newInstance();
      System.out.println("Class loader = "+handler.getClass().getClassLoader());
      System.out.println(handler.getHandlers());
    }
    catch (Exception e) { System.out.println("Uh-oh: "+e); }
  }
}

在这里我们运行它:

$ java -cp .:/usr/share/scala/2.10/lib/scala-library.jar Interop
Class loader = sun.misc.Launcher$AppClassLoader@1f3e8d89
List(salmon, cod)
$

使用 ScalaList和一切,以及由默认 Java 类加载器加载的新类。

于 2013-01-22T20:58:13.433 回答