Search code examples
javareflectionclassloaderjavacompilerabstractmethoderror

AbstractMethodError when calling overridden method of abstract class from a class compiled at runtime


Let me first summarize what exactly I'm trying to do. Basically, I'm using the JavaCompiler package to compile a class at runtime that extends my superclass "Player". The only thing that I know will be in the subclass is that it will extend Player and override the abstract method calcMove(). To load the class at runtime after it has been compiled, I create a URIclassloader object to load the class file after it has been created. The problem is that when I try to run the calcMove method from the instantiated object (by using java.lang.reflect)

Here's bascially what I'm doing:

//to hold the compiler output
ByteArrayOutputStream compilerOut = new ByteArrayOutputStream();

                //compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

int compilationResult = compiler.run(null, compilerOut, compilerOut, playerFile.getAbsolutePath());

if (compilationResult == 0) {

System.out.println("File " + playerFile.getName() + " compiled successfully");

} else {

//code 99 as compile error
System.err.println(compilerOut.toString());
System.exit(99);
}

Once the file is compiled, I use this code to make a uriclassloader to load the class (the upload folder contains the source and class files) The className is determined from the file name. Then I use java Reflection to instantiate the class as an object and cast it as a Player:

URL classUrl = new File("upload").toURI().toURL();

ClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl}, ClassLoader.getSystemClassLoader());
                classLoader.loadClass(className).asSubclass(Player.class);

Class<?> studentCustomClass = Class.forName(className, true, classLoader);


Constructor<?> constructor = studentCustomClass.getConstructor();
Player studentPlayer = (Player) constructor.newInstance()

The instantiation apparently works until I get to where it calls calcMove. I create a new "Game" class that takes 2 Player arguments, within this Game class I call the calcMove() method from the custom class objects (cast as Players). However I'm getting an AbstractMethodError Exception because its trying to call the abstract calcMove method from Player instead of the implemented version from the subclass.

So, I'm wondering, is there some reason why it's trying to call the abstract version from the parent class instead of the version from the class I just compiled? (As far as I can tell, java considers the object I create to be a type of the subclass class rather than just a Player class , which I couldn't instantiate anyway since its abstract) Right now I'm using java Reflection to force it to call the calcMove fucntion from the object

Method calcmove = players[0].getClass().getMethod("calcMove");

calcmove.invoke(players[0]);

I'd like to avoid using reflect at this point in the code for security reasons. So why does this work, but this:

players[0].calcMove();

Gives me an AbstractClassError?


Solution

  • I've managed to find a solution that works. If I declare the abstract method calcMove in Player to be protected, then it properly uses the calcMove from the subclass without using reflection to force it. I'm assuming this is because it can't call the protected method in Player so it calls the public/package access method in the subclass (since overridden methods can have higher levels of visibility than the method they override I've discovered) I'm not entirely sure why this works though.