如何使用Javassist使抽象类非抽象?

如何使用Javassist使抽象类非抽象?,java,classloader,javassist,Java,Classloader,Javassist,在我正在构建的一个小框架中,我想使用Javassist将某些抽象类更改为非抽象类。 我已经在非抽象方法中转换了所有抽象方法,实现了我需要的动态生成的代码。但是我还没有成功地使这个类非抽象化。 我所尝试的是类似于以下内容: 让我们假设c是我想制作的非抽象类。所以我写了: public void instrument(Class c) { ...//some ignored exception management CtClass ctClass = ClassPool.getDef

在我正在构建的一个小框架中,我想使用Javassist将某些抽象类更改为非抽象类。 我已经在非抽象方法中转换了所有抽象方法,实现了我需要的动态生成的代码。但是我还没有成功地使这个类非抽象化。 我所尝试的是类似于以下内容:

让我们假设
c
是我想制作的非抽象类。所以我写了:

public void instrument(Class c) {
    ...//some ignored exception management
    CtClass ctClass = ClassPool.getDefault().get(c.getName());
    ctClass.setModifiers(c.getModifiers() & ~Modifier.ABSTRACT);  
    return ctClass.toClass().newInstance();
}
然而,呼吁:

ctClass.toClass();
正在引发以下
CannotCompileException

"attempted  duplicate class definition for name: <class_name>."
“尝试为名称复制类定义:。”
这是因为类已经被加载,因为我正在调用它的
getName
方法。在我看来,这是我从现有类中获取
CtClass
的唯一机制,但请有人告诉我这是否正确。硬编码类的名称而不是调用其
getName
方法远非理想的解决方案,因为我需要将此例程应用于许多类


有什么解决办法吗?。如果根本不可能,我将动态生成一个新类,扩展抽象类,实现它的构造函数,以及它所有祖先的抽象方法(稍微复杂一点,因此如果我成功地将原始类改为非abstrat类,我将非常高兴).

您是否尝试创建扩展类而不是更改现有类?因此,创建一个类,实现所有方法,并使用
setSuperClass()
使其扩展您的抽象类。

正如您所描述的,问题是您已经加载了试图重新定义的
类。试图重新定义已由类加载器加载的类是非法的

一个选项可能是做一些类加载器的小把戏:创建一个新的类加载器,它没有加载现有的类(父类是系统类加载器),并通过它加载Javassist(使用
CtClass.toClass()
方法)


正如它所建议的,可能有更好的方法来实现您的目标,而创建子类可能是更好的设计。使用接口而不是抽象类是一种选择吗?如果是这样,也是一种选择,它们的优点是您不需要任何第三方库来创建它们。

为什么您需要在运行时实现这样的方法?长话短说:我正在检测某些类,以便其中的某些抽象方法将自动转换为对prolog引擎的查询。这些调用由methods.Ok中的某些注释定义。我这样问是因为这是一个附加用例。我不知道你会怎么修复它,但也许可以做些别的事情。例如,是否有任何原因使您不能在编译时而不是在运行时生成这些类的实现类?不知道如何在编译时生成这些类,您的意思是在文件系统中创建类并将这些类放入类路径中?这比在运行时在内存中生成类容易吗?我认为这取决于您是否要重复使用相同的类。如果类因不可预测的情况而不同,则必须在运行时执行,否则我更喜欢在构建时执行,因为如果需要,我可以检查并逐步执行代码。在构建时生成代码是一种相当常见的做法(根据生成的内容,有不同的方法,您可以使用注释处理器、一些API或自己编写代码)。这就是我在此期间所做的,以防我找不到问题的解决方案。谢谢你的建议。如果这样做行得通,你为什么特别想修改抽象类?扩展类毕竟是设计抽象类的方式,因为将抽象类更改为非抽象类更容易(更快)。我知道,从概念上讲,更好的解决方案实际上是动态创建一个新类,扩展抽象类并覆盖适当的方法和构造函数。谢谢@prunge的反馈。问题:如果我使用另一个类加载器来加载我的类的修改版本,我将在不同的类加载器中拥有同一类的两个不同版本,不是吗?无法从类加载器?卸载类?。我觉得很烦人,我无法从尚未加载的类对象创建CtClass对象,在我调用getName()创建CtClass对象时,该对象将在第二次加载。@Sergio绝对需要小心管理类加载器,尤其是在调用代码中开始强制转换对象时。您还可以从
字符串
名称创建一个CtClass(但是您失去了编译时安全性的好处)。一旦您有了
对象,该类就已加载,并且您无法再修改它。您知道使用ASM我是否应该能够修改已加载的类吗?或者这是jvm的一般限制,没有库可以做到这一点?@Sergio一旦类加载器加载了一个类,就不能重新加载它。唯一的方法是从新的类加载器加载更新的类并使用它。为什么想象一下这样一个场景:不兼容地修改一个类(删除或添加一个字段)——该类中已经存在于内存中的所有对象会发生什么情况?