Javassist: creating an interface that extends another interface with generics

2.2k views Asked by At

I am using javassist in a project and I need to create the following interface at runtime:

package com.example;

import org.springframework.data.repository.CrudRepository;
import com.example.Cat;

public interface CatRepository extends CrudRepository<Cat, Long> {}

While I had no problem creating the interface CatRepository extending CrudRepository, I do not understand (from the docs and from looking at the source code), how to specify com.example.Cat and java.lang.Long as generics types to the super-interface.

Please note that:

  • com.example.Cat: created at runtime using javassist (no problems with that, I have also tested and it works
  • org.springframework.data.repository.CrudRepository: existent class from a library.

If any one could help with that it would be great!

Thanks! Luca

1

There are 1 answers

2
pabrantes On BEST ANSWER

Short Answer

The generic information can be manipulated in Javassist using the SignatureAttribute.

Long answer (with code)

The code you probably already have is something like this:

 ClassPool defaultClassPool = ClassPool.getDefault();
 CtClass superInterface = defaultClassPool.getCtClass(CrudRepository.class
            .getName());
 CtClass catRepositoryInterface = defaultClassPool.makeInterface("CatRepository", ctClass);

// something is missing here :-(

 catRepositoryInterface.toClass()

But, like you already said this will not add the information about generics. In order to achieve the same bytecode you would get from compiling the source code, you need to do the following where the comment is:

SignatureAttribute signatureAttribute = new SignatureAttribute(
            classFile.getConstPool(),
     "Ljava/lang/Object;Lorg/springframework/data/repository/CrudRepository<Lorg/example/Cat;Ljava/lang/Long;>;");
ClassFile metaInformation = catRepositoryInterface.getClassFile();
classFile.addAttribute(signatureAttribute);

Let's break down the signature string in order to understand what's happening there:

  • You see several L[TYPE], what is it? L is the standard notation in bytecode to define an Object Class, you can read more about this if you're interested in the JVM Specification regarding descriptors

  • ';' is being used as a separator between several definitions. Let's look at each one of them:

    • Ljava/lang/Object
    • Lorg/springframework/data/repository/CrudRepository<Lorg/example/Cat;Ljava/lang/Long;>

The first definition has to be there because in the Java language everything extends from java.lang.Object (doesn't matter if class or interface).

But the most interesting one is the second one, there you have your type with the full classname and the generic types definitions, everything using the L notation. That's what you are missing :-)


Note

Keep in mind that if you want to extend from more than one interface, you just have to add them in the list for example, the following signature would make the interface not only extend from CrudRepository but also from Serializable:

Ljava/lang/object;Lorg/springframework/data/repository/CrudRepository<Lorg/example/Cat;Ljava/lang/Long;>;**Ljava/io/Serializable;**