自定义类加载的ClassCastException

时间:2015-07-08 03:23:25

标签: java bukkit

我正在尝试用Java编写脚本系统,并且我设法让我的脚本进行编译和实例化,但当我尝试将脚本转换为“DeftScript”时,它会抛出一个ClassCastError甚至被认为脚本本身扩展了“DeftScript”类

错误(至少是重要部分):

java.lang.ClassCastException: scripts.Compass cannot be cast to com.deft.core.scripts.DeftScript
    at com.deft.core.scripts.DeftScriptManager.instantiate(DeftScriptManager.java:52) ~[?:?]

错误是由此

引起的
deftScript = (DeftScript)obj;

编译和实例化:

public static DeftScript instantiate(String java) {
    File file = new File("./plugins/Deft-Core/scripts/" + java);

    DeftScript deftScript = null;

    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);

    List<String> optionList = new ArrayList<String>();
    optionList.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path") + ";./plugins/Deft-Core.jar"));


    Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(file));
    JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, optionList, null, compilationUnit);
    if (task.call()) {

        Object obj = null;
        try {
            String jarFile = "./plugins/Deft-Core.jar";
            URLClassLoader classLoader = new URLClassLoader (new URL[] {new File(jarFile).toURI().toURL(), new File("./plugins/Deft-Core/").toURI().toURL()}, Thread.currentThread().getContextClassLoader());

            Class<?> loadedClass;
            loadedClass = Class.forName("scripts.Compass", false, classLoader);
            obj = loadedClass.newInstance();

        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | MalformedURLException e) {
            e.printStackTrace();
        } 

        deftScript = (DeftScript)obj;
        deftScript.onEnable();
    } else {
        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
            System.out.format("Error on line %d in %s%n", diagnostic.getLineNumber(), diagnostic.getSource().toUri());
        }
    }

    return deftScript;
}

调用实例化方法:

String script = "Compass.java";
DeftScriptManager.instantiate(script);

DeftScript.java

package com.deft.core.scripts;

public abstract class DeftScript {
    public abstract void onEnable();
}

Compass.java

package scripts;
import com.deft.core.scripts.DeftScript;
public class Compass extends DeftScript {
    @Override
    public void onEnable() {}
}

1 个答案:

答案 0 :(得分:1)

如果你的默认类加载器加载类DeftScript,你加载的.jar也包含类DeftScript,那么Java会认为这是两个不同的类,它们具有相同的二进制名称,但是由不同的类加载器加载,你将获得该异常因为Java看到你试图混合两个不同的类,就像它们是同一个东西。

Java中类的唯一标识包括二进制类名和用于加载类的类加载器。

如果您像这样创建URLClassloader:

URLClassLoader classLoader = 
    new URLClassLoader (new URL[] {new File(jarFile).toURI().toURL(), 
    new File("./plugins/Deft-Core/").toURI().toURL()},
           Thread.currentThread().getContextClassLoader());

第二个参数告诉java首先使用当前线程的类加载器加载类,如果它们未在父类中定义,则只从URLClassLoader中的jar加载它们。

现在类加载器将首先引用它的父类,而类DeftScript只会由父类加载器加载,即使你的.jar文件定义了相同的类(按名称)。

这是一篇非常好的文章,描述了它的工作方式:

http://www2.sys-con.com/ITSG/virtualcd/java/archives/0808/chaudhri/index.html

这也很有帮助

http://www.javaworld.com/article/2077344/core-java/find-a-way-out-of-the-classloader-maze.html?page=1