Question

I am trying to use javassist to programatically create and compile a class (at runtime) that implements an interface.

I get the following Error whenever I invoke an instance of that dynamic class:

java.lang.AbstractMethodError: FooImpl.test()Ljava/lang/Object;

Here's my interface

public class FooBarInterface<T> {
    public T getEntity();
}

Here's a sample Entity

public class FooEntity {

    @Override
    public String toString() {
        return "Hello, Foo!";
    }
}

Here's how I programatically implement the interface

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());

}

If I changed the return type of the implemented method to Object, then I don't get the Error, like this:

CtMethod testMethod = CtNewMethod.make(
    "public Object getEntity(){" +
        "return new com.test.FooEntity();" +
    "}",
    canImpl
);

Then I successfully get the hello, Foo!. I am OK with changing the return type to Object, but I'd like to understand more why returning with type Foo produces AbstractMethodError.

Was it helpful?

Solution

Inside the JVM, methods with different return types are distinct. After type erasure, FooBarEntity.getEntity() has return type Object. Calls via the interface will look specifically for a method with return type Object, hence why your implementation must return Object.

Normally, your Java compiler will create bridge methods that forward the result of the concrete method as the erased type, but apparently Javassist doesn't do this for you (I haven't used Javassist so I'm not sure).

For more on how bridge methods are used to implement type erasure, see the official Java Tutorials topic on bridge methods.

OTHER TIPS

I was having same error. I had a base class in which I declared a new abstract method. I implemented that method on the other classes that were consuming it. Now on debugging I was getting Abstract method error as soon as I was hitting implementation of the method.

Solution-: I figured that base class been consumed by other artifacts too and I didn't overrode newly created abstract method in those artifacts. Since I never build them as I was not changing them, JVM never throws compile time error but on run time exception occurs. On implementing method in other artifacts I was able to get rid of the exception.Basically in my case all child classes didn't have implementation of base class's abstract method.

When you have a parameterized parameter or return type, the Java compiler compiles it as though it was Object and synthesizes a bridge method with the parameterized signature that calls the other one. Or possibly the other way around. You've only synthesized one of them, not both.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top