从运行时编译的 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 访问方法(因为重写的方法比我发现的重写的方法具有更高级别的可见性) 我不完全确定为什么会这样。
让我先总结一下我到底想做什么。基本上,我使用 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 访问方法(因为重写的方法比我发现的重写的方法具有更高级别的可见性) 我不完全确定为什么会这样。