我正在尝试使用javassist以编程方式创建和编译实现接口的类(在运行时)。
每当我调用该动态类的实例时,我都会收到以下错误:
java.lang.AbstractMethodError: FooImpl.test()Ljava/lang/Object;
这是我的界面
public class FooBarInterface<T> {
public T getEntity();
}
这是一个示例实体
public class FooEntity {
@Override
public String toString() {
return "Hello, Foo!";
}
}
以下是我以编程方式实现接口的方法
public void test() {
ClassPool classPool = ClassPool.getDefault();
CtClass testInterface = classPool.get(FooBarInterface.class.getName());
CtClass fooImpl = classPool.makeClass("FooImpl");
fooImpl.addInterface(testInterface);
CtMethod testMethod = CtNewMethod.make(
"public com.test.FooEntity getEntity(){" +
"return new com.test.FooEntity();" +
"}",
canImpl
);
fooImpl.addMethod(testMethod);
fooImpl.writeFile();
TestInterface<FooEntity> test =
(TestInterface<FooEntity>) fooImpl.toClass().newInstance();
System.out.println(test.getEntity());
}
如果我将已实现方法的返回类型更改为Object,那么我不会得到错误,如下所示:
CtMethod testMethod = CtNewMethod.make(
"public Object getEntity(){" +
"return new com.test.FooEntity();" +
"}",
canImpl
);
然后我成功获得hello, Foo!
。我可以将返回类型更改为Object,但我想了解更多为什么返回类型Foo会生成AbstractMethodError
。
答案 0 :(得分:2)
在JVM中,具有不同返回类型的方法是不同的。在类型擦除后,FooBarEntity.getEntity()
返回类型Object
。通过接口调用将专门针对返回类型为Object
的方法,因此您的实现必须返回Object
。
通常,您的Java编译器将创建桥接方法,将具体方法的结果转发为擦除类型,但显然Javassist不会为您执行此操作(我没有使用过Javassist,所以我不确定)。
有关如何使用桥接方法实现类型擦除的更多信息,请参阅official Java Tutorials topic on bridge methods。
答案 1 :(得分:0)
当您有参数化参数或返回类型时,Java编译器将其编译为就像是Object一样,并使用调用另一个参数化签名的参数化签名合成桥接方法。或者可能反过来。你只合成了其中一个,而不是两个。
答案 2 :(得分:0)
我遇到了同样的错误。我有一个基类,我在其中声明了一个新的抽象方法。我在其他正在使用它的类上实现了该方法。现在在调试时,一旦我遇到方法的实现,我就得到了抽象方法错误。
溶液 - : 我认为基类也被其他工件消耗了,并且我没有在这些工件中覆盖新创建的抽象方法。由于我从未构建它们,因为我没有更改它们,因此JVM从不会引发编译时错误,但会在运行时发生异常。 在其他工件中实现方法时,我能够摆脱异常。在我的例子中,所有子类都没有基类的抽象方法的实现。