如何使用泛型动态生成Java子类

如何使用泛型动态生成Java子类,java,cglib,Java,Cglib,我有一个类,我想从中动态生成一个子类,并动态添加适当的泛型。例如,这里有一个我想要扩展的基类 public class Foo<A> { private A attribute; // constructor public Foo(A value) { this.attribute = value; } public A getAttribute() { return attribute;

我有一个类,我想从中动态生成一个子类,并动态添加适当的泛型。例如,这里有一个我想要扩展的基类

public class Foo<A> {

     private A attribute;

     // constructor
     public Foo(A value) {
         this.attribute = value;
     }

     public A getAttribute() {
          return attribute;
     }
}
公共类Foo{
私有属性;
//建造师
公共食品(A值){
this.attribute=值;
}
公共getAttribute(){
返回属性;
}
}
我想动态生成这样的子类,它用指定的类型填充“generic”a值,在本例中使用“Dog”

public class SubClassOfFoo extends Foo<Dog> {

         public SubClassOfFoo(Dog dog) {
              super(dog);
         }
}
public类subassofoo扩展了Foo{
公共子类(狗){
超级(狗);
}
}
我已经看过CGLib,但我不知道如何扩展和添加“泛型”类型。我是否缺少CGLib中的某些内容,或者是否有其他库支持此功能

我想动态生成这样的子类,它填充 “泛型”具有指定类型的值

如果是动态生成,您的意思是在运行时。你不能这么做


在运行时不存在。它们只是编译时的语法糖。

通过字节码生成,Java代码的所有功能都是可能的

我对cglib不太熟悉,但这里有一些asm代码可以满足您的要求

唯一与泛型相关的特殊部分是
genericSig()
方法,它为超类定义了除rawform
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);
    }
}
import org.objectweb.asm.ClassWriter;
导入org.objectweb.asm.MethodVisitor;
导入org.objectweb.asm.opcode;
导入org.objectweb.asm.Type;
导入org.objectweb.asm.signature.SignatureVisitor;
导入org.objectweb.asm.signature.SignatureWriter;
公共类GenExtendFoo{
公共静态void main(字符串[]args)引发异常{
ClassWriter cw=新的ClassWriter(0);
/**使用普通原始超类“com/Foo”扩展Foo
和泛型超类genericSig()Lcom/Foo;
**/
cw.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,“子类”,genericSig(Foo.class,Integer.class),
Type.getInternalName(Foo.class),新字符串[]{};
创建构造函数(cw);
cw.visitEnd();
字节[]b=cw.toByteArray();
类cls=(类)新的MyClassLoader().defineClass(“子类”,b);
Foo instance=(Foo)cls.getConstructor(Integer.class).newInstance(1);
System.out.println(instance.getValue());
/*与普通java版本一样,GenericSuperclass也提供泛型类型*/
ParameterizedType para=(ParameterizedType)实例。getClass().getGenericSuperclass();
System.out.println(第getActualTypeArguments()段[0]);
}
私有静态void createConstructor(ClassWriter cw){
//使用一个调用超类的参数创建构造函数
//带一个参数的构造函数
MethodVisitor mv=cw.visitMethod(opcode.ACC_PUBLIC,“,
“(L”+Type.getInternalName(Integer.class)+“;)V”,null,null);
mv.visitmax(2,2);
mv.visitVarInsn(操作码ALOAD,0);
mv.visitVarInsn(操作码ALOAD,1);
mv.visitMethodInsn(opcode.INVOKESPECIAL,Type.getInternalName(Foo.class),“”,
“(L”+Type.getInternalName(Object.class)+“;)V”,false);//调用
mv.visitInsn(操作码返回);
mv.visitEnd();
}
公共静态字符串genericSig(类mainType、类typeParameter){
SignatureVisitor sv=新的SignatureWriter();
SignatureVisitor psv=sv.visitSuperclass();
psv.visitClassType(Type.getInternalName(mainType));
SignatureVisitor ppsv=psv.visitTypeArgument('=');
ppsv.visitClassType(Type.getInternalName(typeParameter));
ppsv.visitEnd();
psv.visitEnd();
返回sv.toString();
}
}
静态类MyClassLoader扩展了ClassLoader{
公共类定义类(字符串名称,字节[]b){
返回defineClass(名称,b,0,b.length);
}
}

cglib是一个非常古老的库,甚至在讨论泛型之前就创建了它。因此,除非注册ASM访问者并手动添加签名,否则不支持使用库添加通用签名。但是,如果需要,这样做不会添加适当的桥接方法

如果您想创建泛型类,您可以看看(我创建了它,我还随意维护了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();

您想在运行时生成源代码还是类?我更喜欢在运行时动态创建类。如果我必须生成源代码,然后在运行时加载源代码,那么,这是第二个最好的选择。。由于类型擦除,“泛型A”类型在运行时将是
Object
类型。我认为有字节码编写库可以做到这一点。我认为一些JEE容器这样做是为了实现某些功能,比如JPA的实体。但我认为纯Java无法做到这一点(您需要一些JNI代码),而且这确实不容易。类型擦除只从“普通”实例中删除类型信息,而不是从引用或类对象中删除类型信息。@markspace我知道可以使用一些字节码操作框架(如ASM)来完成。但是如果他以这种方式生成类(比如ASM)。那他为什么要用泛型呢?我要指出的是,泛型只是编译时。@k5_u我不理解这种向下投票。OP所问的是IMHO在概念上是错误的。@granmirupa首先一些通用参数是ava
Type type = subclass.getGenericSuperclass();
assert type instanceof ParameterizedType;
assert ((ParameterizedType) type)).getRawType() == Foo.class;
assert ((ParameterizedType) type).getActualTypeArguments()[0] == Dog.class;