I am trying to compile, load and use an inherited class at runtime. Here is the structure of my Java/Maven project. Note there are no .java files under com.mycompany.model.inherited
. This is where generated class files will be placed.
src/main/java
com.mycompany.model.base
BaseClass.java
src/test/java
com.mycompany.model.inherited
BaseClass.java
package com.mycompany.model.base;
public abstract class BaseClass {
public abstract void doWork();
}
At runtime I write the contents of an inherited class to src/test/java/com/mycompany/inherited/SubClass.java
package com.mycompany.model.inherited;
import com.mycompany.model.BaseClass;
public class SubClass extends BaseClass {
@Override
public void doWork() {
System.out.println("SubClass is doing work.");
}
}
And then I execute this code to compile SubClass.java, load it, create an object instance. All of that works fine. But when I try to cast it to a BaseClass I'm getting a ClassCastException
at runtime. Here's the full code.
// prepare the class file contents and write it to disk.
String subClassContents =
"package com.mycompany.model.inherited;\n" +
"\n" +
"import com.mycompany.model.base.BaseClass;\n" +
"\n" +
"public class SubClass extends BaseClass {\n" +
"\n" +
" @Override\n" +
" public void doWork() {\n" +
" System.out.println(\"SubClass is doing work.\");\n" +
" }\n" +
"}\n";
File file = new File(System.getProperty("user.dir") + "\\src\\test\\java\\com\\mycompany\\model\\inherited\\SubClass.java");
FileWriter writer = new FileWriter(file);
writer.write(subClassContents);
writer.close();
// compile the class using the current classpath.
File[] files = new File[]{file};
List<String> options = new ArrayList<>();
options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager filemanager = compiler.getStandardFileManager(null, null, null);
Iterable fileObjects = filemanager.getJavaFileObjects(files);
JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, options, null, fileObjects);
task.call();
// load the class using the current classpath.
String[] paths = System.getProperty("java.class.path").split(";");
List<URL> urls = new ArrayList<>();
urls.add(new File("src/test/java").toURI().toURL());
for (String p : paths) {
urls.add(new File(p).toURI().toURL());
}
URLClassLoader classLoader = URLClassLoader.newInstance(urls.stream().toArray(URL[]::new));
Class<?> cls = Class.forName("com.mycompany.model.inherited.SubClass", true, classLoader);
// use the class.
BaseClass base = (BaseClass) cls.newInstance();
base.doWork();
java.lang.ClassCastException: com.mycompany.model.inherited.SubClass cannot be cast to com.mycompany.model.base.BaseClass
If I include src/test/java/com/mycompany/model/inherited/SubClass.java at compile time it works fine. I.e:
BaseClass base = (BaseClass) SubClass.class.getClassLoader().loadClass("com.mycompany.model.inherited.SubClass").newInstance();
base.doWork();
Hoping someone can point to the issue and help me solve it. Thanks!