Search code examples
javabytecodejavassistbytecode-manipulation

Javassist: Create class that implements generic interface


I´m trying to create a class with javassit that implements a generic interface passing the generic argument but without sucess so far.

I´m aware of the answer Javassist: creating an interface that extends another interface with generics and the javassit tutorial but none of those helped.

Here is my code:

String met = "public void doit(com.package.Pojo o) { System.out.println(\"Result: \" + o.getName()); }";

ClassPool cp = ClassPool.getDefault();

CtClass cc = cp.makeClass("Teste");
cc.setInterfaces(new CtClass[]{cp.get("com.package.Factor")});

ClassSignature cs = new ClassSignature(null, null,
  // Set interface and its generic params
  new ClassType[]{new ClassType("com.package.Factor",
    new TypeArgument[]{new TypeArgument(new ClassType("com.package.Pojo"))}
)});

cc.setGenericSignature(cs.encode());

    //cc.setGenericSignature("Ljava/lang/Object;Lcom/aurora/core/util/Factor<Lcom/aurora/core/util/Pojo;>;");

CtMethod make = CtNewMethod.make(met, cc);
cc.addMethod(make);

Class<Factor<Pojo>> class1 = cc.toClass();
Factor<Pojo> obj = (Factor<Pojo>) class1.newInstance();     
obj.doit(new Pojo("name here"));

Factor class:

public interface Factor<T> {
    void doit(T ent);
}

My pom

<dependency>
  <groupId>org.javassist</groupId>
  <artifactId>javassist</artifactId>
  <version>3.20.0-GA</version>
</dependency>

And the error:

Exception in thread "main" java.lang.AbstractMethodError: Teste.doit(Ljava/lang/Object;)V at com.package.Main3.main(Main3.java:40)

Thanks in advance !


Solution

  • The Java runtime works with type erasure. Therefore, the doit(T) method is defined as doit(java.lang.Object) in the interface type. In order to override a method, you need to implement it in the generated class. The Java compiler does this for you transparently when you define a method, javassist does not do it for you.

    Therefore, you need to add a so-called bridge method that invokes the actual method but that overrides the interface's method:

    public void doit(Object ent) {
      doit((Pojo) ent);
    }