Mit der Bytecode-Generierung ist alles möglich, was mit Java-Code möglich ist.
Ich bin nicht so vertraut mit Cglib, aber hier ist ein Asm-Code, der tun wird, was Sie gefragt haben.
Der einzige spezielle Teil bezogen auf Generics es die genericSig()
Methode, die "eine zweite Signatur" für die Oberklasse neben der Rohform Type.getInternalName(Foo.class)
definiert.
Dies ist keine allgemeine Lösung, es erstellt den Bytecode für das Beispiel in der Frage. Für die allgemeine Lösung müssen viele andere Dinge berücksichtigt werden, insbesondere Brückenmethoden.
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.signature.SignatureVisitor;
import org.objectweb.asm.signature.SignatureWriter;
public class GenExtendFoo {
public static void main(String[] args) throws Exception {
ClassWriter cw = new ClassWriter(0);
/** Extend Foo with normal raw superclass "com/Foo"
And generic superclass genericSig() Lcom/Foo<Ljava/lang/Integer;>;
**/
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "SubClass", genericSig(Foo.class, Integer.class),
Type.getInternalName(Foo.class), new String[] {});
createConstructor(cw);
cw.visitEnd();
byte[] b = cw.toByteArray();
Class<?> cls = (Class<?>) new MyClassLoader().defineClass("SubClass", b);
Foo<Integer> instance = (Foo<Integer>) cls.getConstructor(Integer.class).newInstance(1);
System.out.println(instance.getValue());
/* The generic type is available with GenericSuperclass just like in the plain java version */
ParameterizedType para = (ParameterizedType) instance.getClass().getGenericSuperclass();
System.out.println(para.getActualTypeArguments()[0]);
}
private static void createConstructor(ClassWriter cw) {
// Create constructor with one parameter that calls superclass
// constructor with one parameter
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
"(L" + Type.getInternalName(Integer.class) + ";)V", null, null);
mv.visitMaxs(2, 2);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Foo.class), "<init>",
"(L" + Type.getInternalName(Object.class) + ";)V", false); // call
mv.visitInsn(Opcodes.RETURN);
mv.visitEnd();
}
public static String genericSig(Class<?> mainType, Class<?> typeParameter) {
SignatureVisitor sv = new SignatureWriter();
SignatureVisitor psv = sv.visitSuperclass();
psv.visitClassType(Type.getInternalName(mainType));
SignatureVisitor ppsv = psv.visitTypeArgument('=');
ppsv.visitClassType(Type.getInternalName(typeParameter));
ppsv.visitEnd();
psv.visitEnd();
return sv.toString();
}
}
static class MyClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
Haben Sie Quellcode gene wollen, oder Klassen zur Laufzeit? – NickL
Ich würde es vorziehen, Klassen zur Laufzeit on-the-fly zu erstellen. Wenn ich Quellcode generieren und dann die Quelle zur Laufzeit laden muss, ist das eine zweitbeste Option ... – Jason
Nun ... der 'generische A'-Typ wird zur Laufzeit aufgrund des Typ-Löschens vom Typ 'Object' sein. – NickL