2017-05-02 363 views
0

我有一个类,我想动态生成一个子类并添加适当的通用即时。例如,这是我想要扩展的基类。如何动态生成带有泛型的Java子类

public class Foo<A> { 

    private A attribute; 

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

    public A getAttribute() { 
      return attribute; 
    } 
} 

我要动态地生成这样的一个子类,填补了一个特定类型的“通用”的值,可以说“狗”这个例子。

public class SubClassOfFoo extends Foo<Dog> { 

     public SubClassOfFoo(Dog dog) { 
       super(dog); 
     } 
} 

我看过CGLib,但我看不到如何扩展并向其添加“Generic”类型。我在CGLib中丢失了什么,或者是否有另一个可以使用此功能的库?

+0

你想在运行时generare源代码,或者类? – NickL

+0

我宁愿在运行时即时创建类。如果我必须生成源代码,然后在运行时加载源代码,那么这是第二好的选择...... – Jason

+0

那么......由于类型擦除,'generic A'类型在运行时将是'Object'类型。 – NickL

回答

-1

我要动态地生成这样它填补了 “通用”的值与指定类型

一个子类,如果动态生成你的意思是在运行时。你做不到。

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

+0

我认为有这样的字节码编写库。我认为一些JEE容器是为了实现某些功能而做的,比如JPA的实体。但我认为你不可以用纯Java来完成(你需要一些JNI代码),并且确实不容易。 – markspace

+0

类型擦除仅从“普通”实例中删除类型信息,而不是从引用或类对象中删除类型信息。 –

+0

@markspace我知道它可以使用一些字节码操作框架作为ASM来完成。但是如果他以这种方式生成课程(比方说ASM)。为什么他需要使用通用呢?我指出的是通用只是编译时间。 – granmirupa

-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); 
    } 
} 
+0

这是为什么被拒绝(-2)?如果有人可以评论为什么它被拒绝,我不会浪费时间追求这个例子作为答案。谢谢。 – Jason

+0

@Jason只能猜测。但有一件事是肯定的,我应该提到这个“解决方案”是针对你陈述的问题而写的,远非一般的解决方案。根据扩展类的复杂性,您可能需要引入桥接方法。 –

1

CGLIB是一个非常古老的图书馆和仿制药进行了讨论,甚至之前已创建。因此,除非您注册ASM访问者并手动添加签名,否则不支持使用该库添加通用签名。但是,如果您需要这些,则不会添加适当的桥接方法。

如果你想创建泛型类,你可以看看Byte Buddy(我创造,我也随便保持CGLIB),采用Java的泛型类型系统的所有操作,透明地将所有的桥梁一样javac会做。你可以用字节好友创建示例类,如下所示:

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;