我在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]
抛出一个ClassCastException
,大概是因为Scala编译器没有生成一个干净的Java界面。有没有解决方法,所以我可以有一个Scala专用项目?
答案 0 :(得分: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)
$
使用Scala List
和所有内容,以及默认Java类加载器加载的新类。