【问题标题】:Error: java.lang.AbstractMethodError when invoking a generic method implementing an interface错误:调用实现接口的通用方法时出现 java.lang.AbstractMethodError
【发布时间】:2014-05-12 05:02:26
【问题描述】:

我正在尝试使用 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

【问题讨论】:

  • ClassPool classPool = ClassPool.getDefault(); 这些类不在标准 java 库中。这是第三方库?
  • 是的,你是对的!我正在使用的第三方库是 javassist(虽然在我的问题中提到)

标签: java generics javassist java-bridge-method


【解决方案1】:

在JVM内部,不同返回类型的方法是不同的。类型擦除后,FooBarEntity.getEntity() 的返回类型为 Object。通过接口调用将专门寻找返回类型为Object 的方法,因此您的实现必须返回Object

通常,您的 Java 编译器会创建将具体方法的结果作为已擦除类型转发的桥接方法,但显然 Javassist 不会为您执行此操作(我没有使用 Javassist,所以我不确定)。

有关如何使用桥接方法实现类型擦除的更多信息,请参阅official Java Tutorials topic on bridge methods

【讨论】:

  • 感谢您的信息!在了解了有关类型擦除的更多信息后,我设法找到了答案。
【解决方案2】:

我遇到了同样的错误。我有一个基类,我在其中声明了一个新的抽象方法。我在其他使用它的类上实现了该方法。现在在调试时,我在实现该方法时立即收到抽象方法错误。

解决方案-: 我认为基类也被其他工件所消耗,并且我没有覆盖这些工件中新创建的抽象方法。由于我从未构建它们,因为我没有更改它们,因此 JVM 永远不会引发编译时错误,但会在运行时发生异常。 在其他工件中实现方法时,我能够摆脱异常。基本上在我的情况下,所有子类都没有实现基类的抽象方法。

【讨论】:

  • 就我而言,我不知道在类型擦除期间,当 JVM 编译时,它会自动向子类型(泛型类/接口的)添加一个桥接方法以保留多态性。由于我在运行时使用 javassist 编译类,因此我没有获得自动获取桥接方法的好处 - 或者至少,我不知道如何告诉 javassist 提供桥接方法。
【解决方案3】:

当您有一个参数化参数或返回类型时,Java 编译器会将其编译为 Object,并使用调用另一个参数化签名的桥接方法合成一个桥接方法。或者可能反过来。你只合成了其中一个,而不是两者。

【讨论】:

  • 感谢您的回答!我将尝试检查 javassist 文档,它们是否还支持合成具有特定类型的实现。
猜你喜欢
  • 1970-01-01
  • 2017-04-16
  • 2021-09-29
  • 1970-01-01
  • 2010-11-23
  • 2015-05-11
  • 2017-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多