我有一个类,我想动态生成一个子类,并在运行中添加适当的通用。例如,这是一个我希望扩展的基类。
public class Foo<A> {
private A attribute;
// constructor
public Foo(A value) {
this.attribute = value;
}
public A getAttribute() {
return attribute;
}
}
我想动态生成这样一个子类,它填充&#34; generic&#34;具有指定类型的值,可以说是“狗”和“狗”。对于这个例子。
public class SubClassOfFoo extends Foo<Dog> {
public SubClassOfFoo(Dog dog) {
super(dog);
}
}
我看过CGLib,但我不知道如何扩展和添加一个&#34; Generic&#34;键入它。我在CGLib中遗漏了什么,或者是否有其他能够使用此功能的库?
答案 0 :(得分:2)
cglib是一个非常古老的库,是在甚至讨论泛型之前创建的。因此,除非您注册ASM访问者并手动添加签名,否则不支持使用库添加通用签名。但是,如果需要,请不要添加适当的桥接方法。
如果你想创建泛型类,你可以查看Byte Buddy(我创建的,我也随便维护cglib),它采用Java的通用类型系统进行所有操作,透明地添加所有桥,就像 javac 一样。您可以使用Byte Buddy创建示例类,如下所示:
Class<?> subclass = new ByteBuddy()
.subclass(TypeDescription.Generic.Builder.parameterizedType(Foo.class, Dog.class)
.build())
.make()
.load(Foo.class.getClassLoader())
.getLoaded();
您现在可以检查生成的子类的泛型类型:
Type type = subclass.getGenericSuperclass();
assert type instanceof ParameterizedType;
assert ((ParameterizedType) type)).getRawType() == Foo.class;
assert ((ParameterizedType) type).getActualTypeArguments()[0] == Dog.class;
答案 1 :(得分:-1)
答案 2 :(得分:-2)
使用Java代码可以实现所有可能的字节码生成。
我对cglib并不熟悉,但这里有一些asm代码可以满足您的要求。
与泛型相关的唯一特殊部分是genericSig()
方法,除了原型Type.getInternalName(Foo.class)
之外,还为超类定义了“第二个签名”。
这不是一般解决方案,它会在问题中为示例创建字节码。对于一般解决方案,还需要考虑许多其他事项,尤其是桥接方法。
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);
}
}