Ошибка: java.lang.AbstractMethodError при вызове универсального метода, реализующего интерфейс

Я пытаюсь использовать 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,

3 ответа

Решение

Внутри JVM методы с разными типами возврата различны. После стирания типа, FooBarEntity.getEntity() имеет тип возврата Object, Вызовы через интерфейс будут искать специально метод с типом возврата Objectследовательно, почему ваша реализация должна вернуть Object,

Обычно ваш Java-компилятор создает методы моста, которые пересылают результат конкретного метода как стертый тип, но, очевидно, Javassist не делает этого за вас (я не использовал Javassist, поэтому я не уверен).

Для получения дополнительной информации о том, как методы моста используются для реализации стирания типов, см. Официальный раздел Учебники Java по методам моста.

У меня была такая же ошибка. У меня был базовый класс, в котором я объявил новый абстрактный метод. Я реализовал этот метод в других классах, которые его потребляли. Теперь при отладке я получаю ошибку абстрактного метода, как только я нажимаю на реализацию метода.

Решение: я полагал, что базовый класс использовался и другими артефактами, и я не переопределил вновь созданный абстрактный метод в этих артефактах. Так как я никогда не собираю их, поскольку я не изменял их, JVM никогда не выдает ошибку времени компиляции, но возникает исключение времени выполнения. При реализации метода в других артефактах я смог избавиться от исключения. В основном, в моем случае все дочерние классы не имели реализации абстрактного метода базового класса.

Когда у вас есть параметризованный параметр или возвращаемый тип, компилятор Java компилирует его так, как если бы он был Object, и синтезирует метод моста с параметризованной сигнатурой, которая вызывает другую. Или, возможно, наоборот. Вы только синтезировали один из них, а не оба.

Другие вопросы по тегам