从运行时编译的 class 调用抽象 class 的重写方法时出现 AbstractMethodError

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

让我先总结一下我到底想做什么。基本上,我使用 JavaCompiler 包在 运行 时编译一个 class,它扩展了我的超级 class "Player"。我唯一知道的是在 subclass 中,它将扩展 Player 并覆盖抽象方法 calcMove()。为了在编译后的 运行 时间加载 class,我创建了一个 URIclassloader 对象来在文件创建后加载 class 文件。问题是,当我尝试 运行 来自实例化对象的 calcMove 方法时(通过使用 java.lang.reflect)

这就是我正在做的事情:

//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);
}

编译文件后,我使用此代码制作一个 uriclassloader 来加载 class(上传文件夹包含源文件和 class 文件)class名称由文件名决定。然后我使用 java 反射将 class 实例化为一个对象并将其转换为 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()

在我到达它调用 calcMove 的位置之前,实例化显然有效。我创建了一个新的 "Game" class,它接受 2 个玩家参数,在此游戏 class 中,我从自定义 class 对象(作为玩家)调用 calcMove() 方法。但是我得到一个 AbstractMethodError 异常,因为它试图从 Player 调用抽象的 calcMove 方法而不是从 subclass.

调用实现的版本

所以,我想知道,是否有某种原因导致它试图从父 class 调用抽象版本而不是我刚刚编译的 class 的版本? (据我所知,java 认为我创建的对象是 subclass class 的一种类型,而不仅仅是一个 Player class ,我不能这样做' t 实例化,因为它是抽象的)现在我正在使用 java 反射来强制它从对象

调用 calcMove 函数
Method calcmove = players[0].getClass().getMethod("calcMove");

calcmove.invoke(players[0]);

出于安全原因,我想避免在代码中的此时使用反射。那么为什么这行得通,但是这个:

players[0].calcMove();

给我一个 AbstractClassError?

我设法找到了有效的解决方案。如果我声明 Player 中的抽象方法 calcMove 受到保护,那么它会正确地使用子类中的 calcMove,而不使用反射来强制它。我假设这是因为它不能调用 Player 中的受保护方法,所以它调用子类中的 public/package 访问方法(因为重写的方法比我发现的重写的方法具有更高级别的可见性) 我不完全确定为什么会这样。