创建给定类的Collection的Class对象

问题描述

是否可以人为地创建ParameterizedType对象,该对象将是特定指定类型的集合的定义?如果我有一个命名的Collection字段,则可以有正确的定义,即。用于此类课程中的字段

public class MyContainerClass {
  List<String> myElementsList;
}

我可以通过以下代码提取所有需要的信息。

public class GetGenericsTest {
    public static class MyContainerClass {
        List<String> myElementsList;
    }

    public static void main(String[] args) throws Exception {
        Field field = MyContainerClass.class.getDeclaredField("myElementsList");

        ParameterizedType pt = (ParameterizedType) field.getGenericType();
        System.out.println("collection type: " + pt.getRawType().getTypeName());
        System.out.println("elt type:        " + ((Class<?>)pt.getActualTypeArguments()[0]).getName());
    }
}

产生:

collection type: java.util.List
elt type:        java.lang.String

但是我不知道如何仅通过反射创建这样的ParameterizedType

换句话说,我需要一个通用的解决方案,以便以下测试代码能够通过:

Class<?> elementClass = MyElement.class;

ParameterizedType parameterizedType = implementMe(elementClass,List.class);

Assertions.assertEquals(List.class.getName(),parameterizedType.getRawType().getTypeName());
Assertions.assertEquals(elementClass.getName(),((Class<?>)pt.getActualTypeArguments()[0]).getName());

解决方法

这里是implementMe

public class TypeImpl implements Type {
    private final Class<?> clazz;

    public TypeImpl(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public String getTypeName() {
        return clazz.getName();
    }
}

public ParameterizedType implementMe(Class<?> elementClass,Class<?> collectionClass) {
    return new ParameterizedType() {
        @Override
        public Type[] getActualTypeArguments() {
            return new Type[] {
                    new TypeImpl(elementClass)
            };
        }

        @Override
        public Type getRawType() {
            return new TypeImpl(collectionClass);
        }

        @Override
        public Type getOwnerType() {
            return null;
        }
    };
}

测试:

Class<?> elementClass = String.class;
ParameterizedType parameterizedType = implementMe(elementClass,List.class);
System.out.println(List.class.getName().equals(parameterizedType.getRawType().getTypeName()));//true
System.out.println(elementClass.getName().equals(((Class<?>)pt.getActualTypeArguments()[0]).getName()));//true

但是我不知道如何仅通过反射创建这样的Class

您无法创建自己的Class。这是最后一课,具有私有构造函数。

Class的Javadoc说

类没有公共构造函数。取而代之的是,Java虚拟机会在加载类时以及通过调用类加载器中的defineClass方法自动构造Class对象。

How to extend a final class?(Reflection,Javassist)

How to extend a final class in Java

Is it possible to extend a final class in Java?

如果您尝试通过反射克服对Class构造函数的私有访问,您将拥有SecurityException

Class<Class<?>> classClass = (Class<Class<?>>) (Object) Class.class;
Constructor<Class<?>> constructor = classClass.getDeclaredConstructor(ClassLoader.class);
constructor.setAccessible(true);//java.lang.SecurityException: Cannot make a java.lang.Class constructor accessible
Class<?> newClass = constructor.newInstance(this.getClass().getClassLoader());

还请注意,List<String>List<Integer>这两种类型是不同的,但类是相同的List<?>。因此,您已经有了collectionClass

,

您的问题没有道理。我认为您需要的是简短的泛型简介。

它们是经过编译器检查的注释

仅此而已。编译器将使用它们生成警告,错误和无提示强制转换。然后,编译器将抛出该信息。一个例外是泛型以可能影响其他源文件中的代码编译的类型出现,因此,在签名中出现的任何类型(字段的类型,您拥有的任何extends子句或(任何方法参数的类型或任何方法的返回类型)-但这是由编译器通过将此信息写到类文件中作为“注释”来处理的-JVM本身完全忽略了所有这些内容。不在乎一个iota。

您的建议暗示,在编译时,您无能为力。它是动态的。

但是泛型仅在编译时存在。

因此,您想要的东西将完全没有用。因此,问题的实际答案(即:您想要的是完全不可能的)-实际上不是问题。因为没有必要这样做。

一些其他问题:

containerClass.getGenericSuperclass();

这没有按照您的想法做。 java.lang.Class变量无法保存泛型。自己尝试一下:

List<String> list1 = new ArrayList<String>();
List<Integer> list2 = new ArrayList<Integer>();
sysout(list1.getClass() == list2.getClass();

上面的代码显示'true',从而证明泛型部分不在类变量中。另外,Class<?> c = List<Integer>.class;是编译器错误。

.getGenericSuperclass()为您带来的好处是AbstractList<String>

public class MyClass extends AbstractList<String>

请注意,如果您在数组列表上尝试这种特技,则会得到ArrayList.javaAbstractList<T>的字面意思。

如果要使此方法可行:

public <T> List<T> makeListOf(T type) {
    // .. stuff here
}

such that:

List<String> list = makeListOf(String.class);
assertEquals(list.getClass()
   .getGenericSuperclass().toString(),"AbstractList<String>");

然后,唯一的方法是使用ASM或BCEL或其他字节码创建工具在运行时创建一个全新的类,并通过类加载器的defineClass加载它,从而永久耗尽进程的内存。这绝对没有任何用处,因此我非常怀疑您是否愿意这样做。这也很复杂。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...